package db

import org.flywaydb.core.Flyway
import org.flywaydb.core.api.MigrationInfo
import zio.*
import javax.sql.DataSource

/** Migration status summary
  *
  * @param current
  *   The currently applied migration version (None if no migrations applied)
  * @param pending
  *   Number of pending migrations waiting to be applied
  */
case class MigrationStatus(current: Option[String], pending: Int)

/** Flyway database migration service
  *
  * Wraps Flyway migrations in ZIO effects for controlled execution. Migrations are NOT auto-run on
  * application startup - they must be explicitly invoked via CLI (Phase 5: CLI Scaffolding).
  *
  * Migration files are stored in: resources/db/migration/ Following Flyway naming convention:
  * V{version}__{description}.sql
  *
  * Example: V1__create_summer_users.sql
  */
trait Migrator {

  /** Run all pending migrations
    *
    * @return
    *   Number of migrations applied
    */
  def migrate: Task[Int]

  /** Get current migration status
    *
    * @return
    *   Status with current version and pending count
    */
  def status: Task[MigrationStatus]
}

object Migrator {

  /** Live implementation of Migrator backed by Flyway */
  val live: ZLayer[DataSource, Nothing, Migrator] =
    ZLayer.fromFunction { (ds: DataSource) =>
      new Migrator {
        private def flyway: Flyway =
          Flyway.configure()
            .dataSource(ds)
            .locations("classpath:db/migration")
            .table("summer_migrations")
            .baselineOnMigrate(true)
            .load()

        override def migrate: Task[Int] =
          ZIO.attempt {
            flyway.migrate().migrationsExecuted
          }

        override def status: Task[MigrationStatus] =
          ZIO.attempt {
            val info    = flyway.info()
            val current = Option(info.current()).map(_.getVersion.getVersion)
            val pending = info.pending().length
            MigrationStatus(current, pending)
          }
      }
    }

  /** Accessor for migrate effect */
  def migrate: ZIO[Migrator, Throwable, Int] =
    ZIO.serviceWithZIO[Migrator](_.migrate)

  /** Accessor for status effect */
  def status: ZIO[Migrator, Throwable, MigrationStatus] =
    ZIO.serviceWithZIO[Migrator](_.status)
}
