import Combine
import CoreData
import Foundation

class CipherListViewModel : ObservableObject {
    var cipherService: CipherServiceProtocol
    var watchConnectivityManager = WatchConnectivityManager.shared
    
    @Published private var ciphers: [Cipher] = []
    @Published var filteredCiphers: [Cipher] = []
    @Published var updateHack:Bool = false
    @Published var showingSheet = false
    @Published var currentState = BWState.valid
    @Published var user: User?
    
    @Published var searchTerm: String = ""
    
    var debugText: String? = nil
    
    private var subscriber: AnyCancellable?
    
    init(_ cipherService: CipherServiceProtocol){
        self.cipherService = cipherService
                
        subscriber = watchConnectivityManager.watchConnectivitySubject.sink { completion in
                print("WCM subject: \(completion)")
        } receiveValue: { value in
            self.debugText = value.debugText
            self.checkStateAndFetch(value.state)
        }
        
        Publishers.CombineLatest($ciphers, $searchTerm)
              .map { allCiphers, searchTerm in
                  var returnCiphers = allCiphers.filter { c in
                      self.cipherContains(c, searchTerm)
                  }
                  
                  // WORKAROUND: To display 0 search results
                  if !searchTerm.isEmpty, returnCiphers.count == 0 {
                      returnCiphers.append(Cipher(id: "-1", name: "NoItemsFound", login: Login(username: "", totp: "", uris: nil)))
                  }
                  
                  if searchTerm.isEmpty {
                      self.updateHack = !self.updateHack
                  }
                  
                  return returnCiphers
              }
              .assign(to: &$filteredCiphers)
    }
    
    func checkStateAndFetch(_ state: BWState? = nil) {
        guard checkDeviceOwnerAuth() else {
            return
        }
        
        StateService.shared.checkIntegrity()
        
        user = StateService.shared.getUser()
        
        currentState = state ?? StateService.shared.currentState
        showingSheet = currentState != .valid
        
        if state != nil {
            return
        }
        
        guard currentState == .valid else {
            ciphers = []
            return
        }
        
        self.fetchCiphers()
    }
    
    func fetchCiphers() {
        let c = cipherService.fetchCiphers(user?.id)
        DispatchQueue.main.async {
            self.ciphers = c
        }
    }
    
    func cipherContains(_ cipher: Cipher, _ searchText: String) -> Bool {
        if searchTerm.isEmpty {
            return true
        }
                
        if(cipher.name?.lowercased().contains(searchTerm.lowercased()) ?? false) {
            return true
        }
        
        if (cipher.login.username?.lowercased().contains(searchTerm.lowercased()) ?? false) {
            return true
        }
        
        return false
    }
    
    func checkDeviceOwnerAuth() -> Bool {
        guard KeychainHelper.standard.hasDeviceOwnerAuth() else {
            currentState = .needDeviceOwnerAuth
            showingSheet = true
            StateService.shared.lackedDeviceOwnerAuthLastTime = true
            return false
        }
        
        if StateService.shared.lackedDeviceOwnerAuthLastTime {
            watchConnectivityManager.triggerSync()
            StateService.shared.lackedDeviceOwnerAuthLastTime = false
        }
        
        return true
    }
}
