import Foundation
import Combine

protocol PreferencesRepository {
    func storeRecentSearchTerm(_ term: String)
    func retrieveRecentSearchTerms() -> [String]
    var ignoreExternalWebAssets: Bool { get set }
    var ignoreExternalWebAssetsPublisher: AnyPublisher<Bool, Never> { get }
    var ignoreAppCSS: Bool { get set }
    var ignoreAppCSSPublisher: AnyPublisher<Bool, Never> { get }
    var hasSeenOnboarding: Bool { get set }
    var hasSeenOnboardingPublisher: AnyPublisher<Bool, Never> { get }
    var userPostcode: Int { get set }
}

struct UserDefaultsPreferencesRepository: PreferencesRepository {
    static let standard: Self = {
        Self()
    }()
    
    enum StorageKey: String {
        case searchTerms = "searchTerms"
        case ignoreExternalWebAssets = "ignoreExternalWebAssets"
        case ignoreAppCSS = "ignoreAppCSS"
        case hasSeenOnboarding = "hasSeenOnboarding"
        case userPostcode
    }
    
    var ignoreExternalWebAssetsPublisher = UserDefaults.standard.publisher(for: \.ignoreExternalWebAssets).eraseToAnyPublisher()
    var ignoreAppCSSPublisher = UserDefaults.standard.publisher(for: \.ignoreAppCSS).eraseToAnyPublisher()
    var hasSeenOnboardingPublisher = UserDefaults.standard.publisher(for: \.hasSeenOnboarding).eraseToAnyPublisher()
    
    func storeRecentSearchTerm(_ term: String) {
        var existing: [String] = retrieveRecentSearchTerms()
        
        existing.append(term)
        
        UserDefaults.standard.set(existing.unique(), forKey: StorageKey.searchTerms.rawValue)
    }
    
    func retrieveRecentSearchTerms() -> [String] {
        return (UserDefaults.standard.array(forKey: StorageKey.searchTerms.rawValue) as? [String]) ?? []
    }
    
    var ignoreExternalWebAssets: Bool {
        get {
            UserDefaults.standard.bool(forKey: StorageKey.ignoreExternalWebAssets.rawValue)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: StorageKey.ignoreExternalWebAssets.rawValue)
        }
    }
    
    var ignoreAppCSS: Bool {
        get {
            UserDefaults.standard.bool(forKey: StorageKey.ignoreAppCSS.rawValue)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: StorageKey.ignoreAppCSS.rawValue)
        }
    }
    
    var hasSeenOnboarding: Bool {
        get {
            UserDefaults.standard.bool(forKey: StorageKey.hasSeenOnboarding.rawValue)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: StorageKey.hasSeenOnboarding.rawValue)
        }
    }
    
    var userPostcode: Int {
        get {
            UserDefaults.standard.integer(forKey: StorageKey.userPostcode.rawValue)
        }
        set {
            UserDefaults.standard.set(newValue, forKey: StorageKey.userPostcode.rawValue)
        }
    }
}

extension UserDefaults {
    @objc dynamic var ignoreExternalWebAssets: Bool {
        return bool(forKey: UserDefaultsPreferencesRepository.StorageKey.ignoreExternalWebAssets.rawValue)
    }
    
    @objc dynamic var ignoreAppCSS: Bool {
        return bool(forKey: UserDefaultsPreferencesRepository.StorageKey.ignoreAppCSS.rawValue)
    }
    
    @objc dynamic var hasSeenOnboarding: Bool {
        return bool(forKey: UserDefaultsPreferencesRepository.StorageKey.hasSeenOnboarding.rawValue)
    }
    
    @objc dynamic var userPostcode: Int {
        return integer(forKey: UserDefaultsPreferencesRepository.StorageKey.userPostcode.rawValue)
    }
}
