import UIKit
import Shout
import Socket
import Shared
import Combine
import Foundation
import CloudFiles
import CloudFilesSFTP
import DependencyInjection

struct SFTPViewState {
  var host: String = ""
  var username: String = ""
  var password: String = ""
  var isButtonEnabled: Bool = false
}

final class BackupSFTPViewModel {
  @Dependency var hudController: HUDController

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

  var authPublisher: AnyPublisher<(String, String, String), Never> {
    authSubject.eraseToAnyPublisher()
  }

  private let stateSubject = CurrentValueSubject<SFTPViewState, Never>(.init())
  private let authSubject = PassthroughSubject<(String, String, String), Never>()

  func didEnterHost(_ string: String) {
    stateSubject.value.host = string
    validate()
  }

  func didEnterUsername(_ string: String) {
    stateSubject.value.username = string
    validate()
  }

  func didEnterPassword(_ string: String) {
    stateSubject.value.password = string
    validate()
  }

  func didTapLogin() {
    hudController.show()

    let host = stateSubject.value.host
    let username = stateSubject.value.username
    let password = stateSubject.value.password

    let anyController = UIViewController()

    DispatchQueue.global().async { [weak self] in
      guard let self else { return }
      do {
        try CloudFilesManager.sftp(
          host: host,
          username: username,
          password: password,
          fileName: "backup.xxm"
        ).link(anyController) {
          switch $0 {
          case .success:
            self.hudController.dismiss()
            self.authSubject.send((host, username, password))
          case .failure(let error):
            var message = "An error occurred while trying to link SFTP: "

            if case let CloudFilesSFTP.SFTP.SFTPError.link(linkError) = error {
              if let sshError = linkError as? SSHError {
                message.append(sshError.message)
              } else if let socketError = linkError as? Socket.Error, let reason = socketError.errorReason {
                message.append(reason)
              } else {
                message.append(error.localizedDescription)
              }
            } else {
              message.append(error.localizedDescription)
            }

            self.hudController.show(.init(content: message))
          }
        }
      } catch {
        self.hudController.show(.init(error: error))
      }
    }
  }

  private func validate() {
    stateSubject.value.isButtonEnabled =
    !stateSubject.value.host.isEmpty &&
    !stateSubject.value.username.isEmpty &&
    !stateSubject.value.password.isEmpty
  }
}