/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * license agreements; and to You under the Apache License, version 2.0:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * This file is part of the Apache Pekko project, which was derived from Akka.
 */

/*
 * Copyright (C) since 2016 Lightbend Inc. <https://www.lightbend.com>
 */

package docs.scaladsl

import java.nio.file.{ Files, Path }

import org.apache.pekko
import pekko.NotUsed
import pekko.actor.ActorSystem
import pekko.stream.connectors.testkit.scaladsl.LogCapturing
import pekko.stream.scaladsl.{ Flow, FlowWithContext, Sink, Source }
import pekko.stream.testkit.scaladsl.StreamTestKit.assertAllStagesStopped
import pekko.testkit.TestKit
import com.google.common.jimfs.{ Configuration, Jimfs }
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.BeforeAndAfterAll

import scala.collection.immutable
import scala.concurrent.Future
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpecLike

class DirectorySpec
    extends TestKit(ActorSystem("directoryspec"))
    with AnyWordSpecLike
    with Matchers
    with BeforeAndAfterAll
    with ScalaFutures
    with LogCapturing {

  private val fs = Jimfs.newFileSystem(Configuration.forCurrentPlatform.toBuilder.build)

  "The directory source factory" should assertAllStagesStopped {
    "list files" in {
      val dir = fs.getPath("listfiles")
      Files.createDirectories(dir)
      val paths = (0 to 100).map { n =>
        val name = s"file$n"
        Files.createFile(dir.resolve(name))
      }

      // #ls
      import org.apache.pekko.stream.connectors.file.scaladsl.Directory

      val source: Source[Path, NotUsed] = Directory.ls(dir)
      // #ls

      val result = source.runWith(Sink.seq).futureValue
      result.toSet shouldEqual paths.toSet
    }

    "walk a file tree" in {
      val root = fs.getPath("walk")
      Files.createDirectories(root)
      val subdir1 = root.resolve("subdir1")
      Files.createDirectories(subdir1)
      val file1 = subdir1.resolve("file1")
      Files.createFile(file1)
      val subdir2 = root.resolve("subdir2")
      Files.createDirectories(subdir2)
      val file2 = subdir2.resolve("file2")
      Files.createFile(file2)

      // #walk
      import org.apache.pekko.stream.connectors.file.scaladsl.Directory
      import java.nio.file.FileVisitOption

      val files: Source[Path, NotUsed] = Directory.walk(root)

      val files2: Source[Path, NotUsed] = Directory.walk(root, maxDepth = Some(1), List(FileVisitOption.FOLLOW_LINKS))
      // #walk

      val result = files.runWith(Sink.seq).futureValue
      result shouldEqual List(root, subdir1, file1, subdir2, file2)

      val result2 = files2.runWith(Sink.seq).futureValue
      result2 shouldEqual List(root, subdir1, subdir2)
    }

    "create directories" in {
      val dir = fs.getPath("mkdirsScaladsl")
      Files.deleteIfExists(dir)
      Files.createDirectories(dir)
      // #mkdirs
      import org.apache.pekko.stream.connectors.file.scaladsl.Directory

      val flow: Flow[Path, Path, NotUsed] = Directory.mkdirs()

      val created: Future[immutable.Seq[Path]] =
        Source(immutable.Seq(dir.resolve("dirA"), dir.resolve("dirB")))
          .via(flow)
          .runWith(Sink.seq)
      // #mkdirs

      created.futureValue should have size 2
      Files.isDirectory(dir.resolve("dirA")) shouldBe true
      Files.isDirectory(dir.resolve("dirB")) shouldBe true
    }

    "create directories with context" in {
      val dir = fs.getPath("mkdirsScaladsl2")
      Files.deleteIfExists(dir)
      Files.createDirectories(dir)
      import org.apache.pekko.stream.connectors.file.scaladsl.Directory
      // #mkdirs

      val flowWithContext: FlowWithContext[Path, SomeContext, Path, SomeContext, NotUsed] =
        Directory.mkdirsWithContext[SomeContext]()
      // #mkdirs
      val created: Future[immutable.Seq[(Any, Any)]] =
        Source(immutable.Seq(dir.resolve("dirA"), dir.resolve("dirB")))
          .asSourceWithContext(_ => SomeContext())
          .via(flowWithContext)
          .runWith(Sink.seq)

      created.futureValue should have size 2
      Files.isDirectory(dir.resolve("dirA")) shouldBe true
      Files.isDirectory(dir.resolve("dirB")) shouldBe true
    }

  }

  override protected def afterAll(): Unit =
    fs.close()
}

case class SomeContext()
