

package quasar.physical

import slamdata.Predef._
import quasar.connector.{EnvironmentError, EnvErr}
import quasar.config.{CfgErr, ConfigError}
import quasar.effect.Failure
import quasar.fp._
import quasar.fp.free._
import quasar.fs._
import quasar.fs.mount.BackendDef

import scalaz.{Failure => _, _}, Scalaz._
import scalaz.concurrent.Task

object filesystems {
  type Eff[A]  = (CfgErr :\: EnvErr :\: PhysErr :/: Task)#M[A]
  type EffM[A] = Free[Eff, A]

  def testFileSystem(
    f: Free[Eff, BackendDef.DefinitionError \/ BackendDef.DefinitionResult[Free[Eff, ?]]]
  ): Task[(BackendEffect ~> Task, Task[Unit])] = {
    val fsDef = f.flatMap[BackendDef.DefinitionResult[EffM]] {
        case -\/(-\/(strs)) => injectFT[Task, Eff].apply(Task.fail(new RuntimeException(strs.list.toList.mkString)))
        case -\/(\/-(err))  => injectFT[Task, Eff].apply(Task.fail(new RuntimeException(err.shows)))
        case \/-(d)         => d.point[EffM]
      }

    effMToTask(fsDef).map(d =>
      (effMToTask compose d.run,
        effMToTask(d.close)))
  }

  ////

  private val envErr = Failure.Ops[EnvironmentError, Eff]

  private val effToTask: Eff ~> Task =
    Failure.toRuntimeError[Task, ConfigError]      :+:
    Failure.toRuntimeError[Task, EnvironmentError] :+:
    Failure.toRuntimeError[Task, PhysicalError]    :+:
    NaturalTransformation.refl

  private val effMToTask: EffM ~> Task =
    foldMapNT(effToTask)

}
