import Dependencies
import DependenciesMacros
import Elementary
import Logging
import ManualDCore

extension DependencyValues {
  public var viewController: ViewController {
    get { self[ViewController.self] }
    set { self[ViewController.self] = newValue }
  }
}

public typealias AnySendableHTML = (any HTML & Sendable)

@DependencyClient
public struct ViewController: Sendable {

  public typealias AuthenticateHandler = @Sendable (User) -> Void
  public typealias CurrentUserHandler = @Sendable () throws -> User

  public var view: @Sendable (Request) async throws -> AnySendableHTML
}

extension ViewController {

  public struct Request: Sendable {

    public let route: SiteRoute.View
    public let isHtmxRequest: Bool
    public let logger: Logger
    public let authenticateUser: AuthenticateHandler
    public let currentUser: CurrentUserHandler

    public init(
      route: SiteRoute.View,
      isHtmxRequest: Bool,
      logger: Logger,
      authenticateUser: @escaping AuthenticateHandler,
      currentUser: @escaping CurrentUserHandler
    ) {
      self.route = route
      self.isHtmxRequest = isHtmxRequest
      self.logger = logger
      self.authenticateUser = authenticateUser
      self.currentUser = currentUser
    }

  }
}

extension ViewController: DependencyKey {
  public static let testValue = Self()

  // FIX: Fix.
  public static let liveValue = Self(
    view: { request in
      await request.render()
    }
  )
}

extension ViewController.Request {

  func authenticate(
    _ login: User.Login
  ) async throws -> User {
    @Dependency(\.database.users) var users
    let token = try await users.login(login)
    let user = try await users.get(token.userID)!
    authenticateUser(user)
    logger.debug("Logged in user: \(user.id)")
    return user
  }

  @discardableResult
  func createAndAuthenticate(
    _ signup: User.Create
  ) async throws -> User {
    @Dependency(\.database.users) var users
    let user = try await users.create(signup)
    let _ = try await users.login(
      .init(email: signup.email, password: signup.password)
    )
    authenticateUser(user)
    logger.debug("Created and logged in user: \(user.id)")
    return user
  }
}
