import HUD
import Shared
import Models
import Combine
import Defaults
import XXClient
import Foundation
import InputField
import BackupFeature
import CombineSchedulers
import XXMessengerClient
import DependencyInjection

struct ProfileCodeViewState: Equatable {
  var input: String = ""
  var status: InputField.ValidationStatus = .unknown(nil)
  var resendDebouncer: Int = 0
}

final class ProfileCodeViewModel {
  @Dependency var messenger: Messenger
  @Dependency var backupService: BackupService

  @KeyObject(.email, defaultValue: nil) var email: String?
  @KeyObject(.phone, defaultValue: nil) var phone: String?

  let confirmation: AttributeConfirmation

  var timer: Timer?

  var completionPublisher: AnyPublisher<AttributeConfirmation, Never> { completionRelay.eraseToAnyPublisher() }
  private let completionRelay = PassthroughSubject<AttributeConfirmation, Never>()

  var hud: AnyPublisher<HUDStatus, Never> { hudRelay.eraseToAnyPublisher() }
  private let hudRelay = CurrentValueSubject<HUDStatus, Never>(.none)

  var state: AnyPublisher<ProfileCodeViewState, Never> { stateRelay.eraseToAnyPublisher() }
  private let stateRelay = CurrentValueSubject<ProfileCodeViewState, Never>(.init())

  var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()

  init(_ confirmation: AttributeConfirmation) {
    self.confirmation = confirmation
    didTapResend()
  }

  func didInput(_ string: String) {
    stateRelay.value.input = string
    validate()
  }

  func didTapResend() {
    guard stateRelay.value.resendDebouncer == 0 else { return }

    stateRelay.value.resendDebouncer = 60

    timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {  [weak self] in
      guard let self = self, self.stateRelay.value.resendDebouncer > 0 else {
        $0.invalidate()
        return
      }

      self.stateRelay.value.resendDebouncer -= 1
    }
  }

  func didTapNext() {
    hudRelay.send(.on)

    backgroundScheduler.schedule { [weak self] in
      guard let self = self else { return }

      do {
        try self.messenger.ud.get()!.confirmFact(
          confirmationId: self.confirmation.confirmationId!,
          code: self.stateRelay.value.input
        )

        if self.confirmation.isEmail {
          self.email = self.confirmation.content
        } else {
          self.phone = self.confirmation.content
        }

        self.timer?.invalidate()
        self.hudRelay.send(.none)
        self.completionRelay.send(self.confirmation)

        self.backupService.performBackup()
      } catch {
        self.hudRelay.send(.error(.init(with: error)))
      }
    }
  }

  private func validate() {
    switch Validator.code.validate(stateRelay.value.input) {
    case .success:
      stateRelay.value.status = .valid(nil)
    case .failure(let error):
      stateRelay.value.status = .invalid(error)
    }
  }
}