package docspell.backend

import scala.concurrent.ExecutionContext

import cats.effect._

import docspell.backend.auth.Login
import docspell.backend.ops._
import docspell.backend.signup.OSignup
import docspell.ftsclient.FtsClient
import docspell.joexapi.client.JoexClient
import docspell.store.Store
import docspell.store.queue.JobQueue
import docspell.store.usertask.UserTaskStore

import emil.javamail.{JavaMailEmil, Settings}
import org.http4s.client.Client
import org.http4s.client.blaze.BlazeClientBuilder

trait BackendApp[F[_]] {

  def login: Login[F]
  def signup: OSignup[F]
  def collective: OCollective[F]
  def source: OSource[F]
  def tag: OTag[F]
  def equipment: OEquipment[F]
  def organization: OOrganization[F]
  def upload: OUpload[F]
  def node: ONode[F]
  def job: OJob[F]
  def item: OItem[F]
  def itemSearch: OItemSearch[F]
  def fulltext: OFulltext[F]
  def mail: OMail[F]
  def joex: OJoex[F]
  def userTask: OUserTask[F]
  def folder: OFolder[F]
  def customFields: OCustomFields[F]
}

object BackendApp {

  def create[F[_]: ConcurrentEffect: ContextShift](
      cfg: Config,
      store: Store[F],
      httpClient: Client[F],
      ftsClient: FtsClient[F],
      blocker: Blocker
  ): Resource[F, BackendApp[F]] =
    for {
      utStore        <- UserTaskStore(store)
      queue          <- JobQueue(store)
      loginImpl      <- Login[F](store)
      signupImpl     <- OSignup[F](store)
      joexImpl       <- OJoex(JoexClient(httpClient), store)
      collImpl       <- OCollective[F](store, utStore, queue, joexImpl)
      sourceImpl     <- OSource[F](store)
      tagImpl        <- OTag[F](store)
      equipImpl      <- OEquipment[F](store)
      orgImpl        <- OOrganization(store)
      uploadImpl     <- OUpload(store, queue, cfg.files, joexImpl)
      nodeImpl       <- ONode(store)
      jobImpl        <- OJob(store, joexImpl)
      itemImpl       <- OItem(store, ftsClient, queue, joexImpl)
      itemSearchImpl <- OItemSearch(store)
      fulltextImpl   <- OFulltext(itemSearchImpl, ftsClient, store, queue, joexImpl)
      javaEmil =
        JavaMailEmil(blocker, Settings.defaultSettings.copy(debug = cfg.mailDebug))
      mailImpl         <- OMail(store, javaEmil)
      userTaskImpl     <- OUserTask(utStore, queue, joexImpl)
      folderImpl       <- OFolder(store)
      customFieldsImpl <- OCustomFields(store)
    } yield new BackendApp[F] {
      val login        = loginImpl
      val signup       = signupImpl
      val collective   = collImpl
      val source       = sourceImpl
      val tag          = tagImpl
      val equipment    = equipImpl
      val organization = orgImpl
      val upload       = uploadImpl
      val node         = nodeImpl
      val job          = jobImpl
      val item         = itemImpl
      val itemSearch   = itemSearchImpl
      val fulltext     = fulltextImpl
      val mail         = mailImpl
      val joex         = joexImpl
      val userTask     = userTaskImpl
      val folder       = folderImpl
      val customFields = customFieldsImpl
    }

  def apply[F[_]: ConcurrentEffect: ContextShift](
      cfg: Config,
      connectEC: ExecutionContext,
      httpClientEc: ExecutionContext,
      blocker: Blocker
  )(ftsFactory: Client[F] => Resource[F, FtsClient[F]]): Resource[F, BackendApp[F]] =
    for {
      store      <- Store.create(cfg.jdbc, connectEC, blocker)
      httpClient <- BlazeClientBuilder[F](httpClientEc).resource
      ftsClient  <- ftsFactory(httpClient)
      backend    <- create(cfg, store, httpClient, ftsClient, blocker)
    } yield backend
}
