import Shared
import Combine
import Defaults
import XXModels
import Keychain
import XXClient
import CloudFiles
import Foundation
import Permissions
import BackupFeature
import VersionChecking
import ReportingFeature
import CombineSchedulers
import CloudFilesDropbox
import XXMessengerClient

import class XXClient.Cancellable

final class LaunchViewModel {
  struct UpdateModel {
    let content: String
    let urlString: String
    let positiveActionTitle: String
    let negativeActionTitle: String?
    let actionStyle: CapsuleButtonStyle
  }

  struct ViewState {
    var shouldShowTerms = false
    var shouldPushChats = false
    var shouldOfferUpdate: UpdateModel?
    var shouldPushOnboarding = false
  }

  @Dependency var database: Database
  @Dependency var messenger: Messenger
  @Dependency var versionCheck: VersionCheck
  @Dependency var hudController: HUDController
  @Dependency var backupService: BackupService
  @Dependency var fetchBannedList: FetchBannedList
  @Dependency var reportingStatus: ReportingStatus
  @Dependency var toastController: ToastController
  @Dependency var keychainHandler: KeychainHandling
  @Dependency var networkMonitor: NetworkMonitoring
  @Dependency var processBannedList: ProcessBannedList
  @Dependency var permissionHandler: PermissionHandling

  @KeyObject(.username, defaultValue: nil) var username: String?
  @KeyObject(.biometrics, defaultValue: false) var isBiometricsOn: Bool
  @KeyObject(.acceptedTerms, defaultValue: false) var didAcceptTerms: Bool
  @KeyObject(.dummyTrafficOn, defaultValue: false) var dummyTrafficOn: Bool

  var authCallbacksCancellable: Cancellable?
  var backupCallbackCancellable: Cancellable?
  var networkCallbacksCancellable: Cancellable?
  var messageListenerCallbacksCancellable: Cancellable?

  var statePublisher: AnyPublisher<ViewState, Never> {
    stateSubject.eraseToAnyPublisher()
  }

  private var scheduler: AnySchedulerOf<DispatchQueue> = {
    DispatchQueue.global().eraseToAnyScheduler()
  }()

  let dropboxManager = CloudFilesManager.dropbox(
    appKey: "ppx0de5f16p9aq2",
    path: "/backup/backup.xxm"
  )

  let sftpManager = CloudFilesManager.sftp(
    host: "",
    username: "",
    password: "",
    fileName: ""
  )

  let stateSubject = CurrentValueSubject <ViewState, Never>(.init())

  func viewDidAppear() {
    scheduler.schedule(after: .init(.now() + 1)) { [weak self] in
      guard let self else { return }
      self.startLaunch()
    }
  }

  private func startLaunch() {
    if !didAcceptTerms {
      stateSubject.value.shouldShowTerms = true
    }
    hudController.show()
    versionCheck.verify { [weak self] in
      guard let self else { return }
      switch $0 {
      case .upToDate:
        self.didVerifyVersion()
      case .failure(let error):
        self.hudController.show(.init(
          title: Localized.Launch.Version.failed,
          content: error.localizedDescription
        ))
      case .outdated(let info):
        self.hudController.dismiss()
        let isRequired = info.isRequired ?? false

        let content = isRequired ?
        info.minimumMessage :
        Localized.Launch.Version.Recommended.title

        let positiveActionTitle = isRequired ?
        Localized.Launch.Version.Required.positive :
        Localized.Launch.Version.Recommended.positive

        self.stateSubject.value.shouldOfferUpdate = .init(
          content: content,
          urlString: info.appUrl,
          positiveActionTitle: positiveActionTitle,
          negativeActionTitle: isRequired ? nil : Localized.Launch.Version.Recommended.negative,
          actionStyle: isRequired ? .brandColored : .simplestColoredRed
        )
      }
    }
  }

  func didRefuseUpdating() {
    hudController.show()
    didVerifyVersion()
  }

  private func didVerifyVersion() {
    updateBannedList { [weak self] in
      guard let self else { return }
      self.updateErrors {
        switch $0 {
        case .success:
          self.didFinishAsyncWork()
        case .failure(let error):
          self.hudController.show(.init(error: error))
        }
      }
    }
  }

  private func didFinishAsyncWork() {
    do {
      try setupDatabase()
      try setupMessenger()
    } catch {
      let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription)
      hudController.show(.init(content: xxError))
    }
  }

  private func checkBiometrics(completion: @escaping (Result<Bool, Error>) -> Void) {
    if permissionHandler.isBiometricsAvailable && isBiometricsOn {
      permissionHandler.requestBiometrics {
        switch $0 {
        case .success(let granted):
          completion(.success(granted))
        case .failure(let error):
          completion(.failure(error))
        }
      }
    } else {
      completion(.success(true))
    }
  }
}