import HUD
import UIKit
import Models
import Shared
import Combine
import XXClient
import Defaults
import Foundation

import DependencyInjection

import CloudFiles

enum BackupActionState {
  case backupFinished
  case backupAllowed(Bool)
  case backupInProgress(Float, Float)
}

struct BackupConfigViewModel {
  var didTapBackupNow: () -> Void
  var didChooseWifiOnly: (Bool) -> Void
  var didChooseAutomatic: (Bool) -> Void
  var didToggleService: (UIViewController, CloudService, Bool) -> Void
  var didTapService: (CloudService, UIViewController) -> Void

  var wifiOnly: () -> AnyPublisher<Bool, Never>
  var automatic: () -> AnyPublisher<Bool, Never>
  var lastBackup: () -> AnyPublisher<Fetch.Metadata?, Never>
  var actionState: () -> AnyPublisher<BackupActionState, Never>
  var enabledService: () -> AnyPublisher<CloudService?, Never>
  var connectedServices: () -> AnyPublisher<Set<CloudService>, Never>
}

extension BackupConfigViewModel {
  static func live() -> Self {
    class Context {
      @Dependency var hud: HUD
      @Dependency var service: BackupService
      @Dependency var coordinator: BackupCoordinating
    }

    let context = Context()

    return .init(
      didTapBackupNow: {
        context.service.didForceBackup()
        context.hud.update(with: .on)
        DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
          context.hud.update(with: .none)
        }
      },
      didChooseWifiOnly: context.service.didSetWiFiOnly(enabled:),
      didChooseAutomatic: context.service.didSetAutomaticBackup(enabled:),
      didToggleService: { controller, service, enabling in
        guard enabling == true else {
          context.service.toggle(service: service, enabling: false)
          context.service.stopBackups()
          return
        }
        context.coordinator.toPassphrase(from: controller, cancelClosure: {
          context.service.toggle(service: service, enabling: false)
        }, passphraseClosure: { passphrase in
          context.hud.update(with: .onTitle("Initializing and securing your backup file will take few seconds, please keep the app open."))
          context.service.toggle(service: service, enabling: enabling)
          context.service.initializeBackup(passphrase: passphrase)
          context.hud.update(with: .none)
        })
      },
      didTapService: { service, controller in
        if service == .sftp {
          context.coordinator.toSFTP(from: controller) { host, username, password in
            context.service.setupSFTP(host: host, username: username, password: password)
          }
          return
        }

        context.service.authorize(service: service, presenting: controller)
      },
      wifiOnly: {
        context.service.settingsPublisher
          .map(\.wifiOnlyBackup)
          .eraseToAnyPublisher()
      },
      automatic: {
        context.service.settingsPublisher
          .map(\.automaticBackups)
          .eraseToAnyPublisher()
      },
      lastBackup: {
        context.service.settingsPublisher
          .combineLatest(context.service.backupsPublisher)
          .map { settings, backups in
            guard let enabled = settings.enabledService else { return nil }
            return backups[enabled]
          }.eraseToAnyPublisher()
      },
      actionState: {
        context.service.settingsPublisher
          .map(\.enabledService)
          .map { BackupActionState.backupAllowed($0 != nil) }
          .eraseToAnyPublisher()
      },
      enabledService: {
        context.service.settingsPublisher
          .map(\.enabledService)
          .eraseToAnyPublisher()
      },
      connectedServices: {
        context.service.connectedServicesPublisher
          .removeDuplicates()
          .eraseToAnyPublisher()
      }
    )
  }
}