import UIKit
import Shared
import AppCore
import Combine
import Defaults
import XXModels
import Defaults
import XXClient
import AppResources
import Dependencies
import ReportingFeature
import CombineSchedulers
import XXMessengerClient

struct RequestSent: Hashable, Equatable {
  var request: Request
  var isResent: Bool = false
}

final class RequestsSentViewModel {
  @Dependency(\.app.dbManager) var dbManager
  @Dependency(\.app.messenger) var messenger
  @Dependency(\.app.hudManager) var hudManager
  @Dependency(\.app.toastManager) var toastManager
  @Dependency(\.reportingStatus) var reportingStatus
  
  @KeyObject(.username, defaultValue: nil) var username: String?
  @KeyObject(.sharingEmail, defaultValue: false) var sharingEmail: Bool
  @KeyObject(.sharingPhone, defaultValue: false) var sharingPhone: Bool

  var itemsPublisher: AnyPublisher<NSDiffableDataSourceSnapshot<Section, RequestSent>, Never> {
    itemsSubject.eraseToAnyPublisher()
  }
  
  private var cancellables = Set<AnyCancellable>()
  private let itemsSubject = CurrentValueSubject<NSDiffableDataSourceSnapshot<Section, RequestSent>, Never>(.init())
  
  var backgroundScheduler: AnySchedulerOf<DispatchQueue> = DispatchQueue.global().eraseToAnyScheduler()
  
  init() {
    let query = Contact.Query(
      authStatus: [
        .requested,
        .requesting
      ],
      isBlocked: reportingStatus.isEnabled() ? false : nil,
      isBanned: reportingStatus.isEnabled() ? false : nil
    )
    
    try! dbManager.getDB().fetchContactsPublisher(query)
      .replaceError(with: [])
      .removeDuplicates()
      .map { data -> NSDiffableDataSourceSnapshot<Section, RequestSent> in
        var snapshot = NSDiffableDataSourceSnapshot<Section, RequestSent>()
        snapshot.appendSections([.appearing])
        snapshot.appendItems(data.map { RequestSent(request: .contact($0)) }, toSection: .appearing)
        return snapshot
      }.sink { [unowned self] in itemsSubject.send($0) }
      .store(in: &cancellables)
  }
  
  func didTapStateButtonFor(request item: RequestSent) {
    guard case let .contact(contact) = item.request,
          item.request.status == .requested ||
            item.request.status == .requesting ||
            item.request.status == .failedToRequest else {
      return
    }
    
    let name = (contact.nickname ?? contact.username) ?? ""
    
    hudManager.show()
    backgroundScheduler.schedule { [weak self] in
      guard let self else { return }
      
      do {
        var includedFacts: [Fact] = []
        let myFacts = try self.messenger.ud.get()!.getFacts()
        
        if let fact = myFacts.get(.username) {
          includedFacts.append(fact)
        }
        
        if self.sharingEmail, let fact = myFacts.get(.email) {
          includedFacts.append(fact)
        }
        
        if self.sharingPhone, let fact = myFacts.get(.phone) {
          includedFacts.append(fact)
        }
        
        let _ = try self.messenger.e2e.get()!.requestAuthenticatedChannel(
          partner: .live(contact.marshaled!),
          myFacts: includedFacts
        )
        
        self.hudManager.hide()
        
        var item = item
        var allRequests = self.itemsSubject.value.itemIdentifiers
        
        if let indexOfRequest = allRequests.firstIndex(of: item) {
          allRequests.remove(at: indexOfRequest)
        }
        
        item.isResent = true
        allRequests.append(item)
        
        self.toastManager.enqueue(.init(
          title: Localized.Requests.Sent.Toast.resent(name),
          leftImage: Asset.requestSentToaster.image
        ))
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, RequestSent>()
        snapshot.appendSections([.appearing])
        snapshot.appendItems(allRequests, toSection: .appearing)
        self.itemsSubject.send(snapshot)
      } catch {
        self.toastManager.enqueue(.init(
          title: Localized.Requests.Sent.Toast.resentFailed(name),
          leftImage: Asset.requestFailedToaster.image
        ))
        
        let xxError = CreateUserFriendlyErrorMessage.live(error.localizedDescription)
        self.hudManager.show(.init(content: xxError))
      }
    }
  }
}