//
//  Settings.swift
//  App
//
//  Created by James Magahern on 3/9/21.
//

import Foundation

@propertyWrapper
public struct SettingProperty<T: RawRepresentable>
{
    public var key: String
    public var defaultValue: T
    public init(wrappedValue: T, key: String) {
        self.key = key
        self.defaultValue = wrappedValue
    }
    
    public var wrappedValue: T {
        get {
            guard let rawValue = UserDefaults.standard.object(forKey: key) as? T.RawValue else { return defaultValue }
            return T(rawValue: rawValue) ?? defaultValue
        }
        set {
            UserDefaults.standard.setValue(newValue.rawValue, forKey: key)
        }
    }
}

// - These coercions into RawRepresentable are stupid. How do I write specializations for each type instead? 

extension Dictionary: RawRepresentable where Key == String, Value == String {
    public typealias RawValue = [String: String]
    
    public init?(rawValue: [String : String]) {
        self.init()
        self.merge(rawValue, uniquingKeysWith: { $1 })
    }
    
    public var rawValue: [String : String] {
        return self
    }
}

extension Optional: RawRepresentable where Wrapped == String {
    public typealias RawValue = String?
    
    public init?(rawValue: String?) {
        if let rawValue {
            self = String(rawValue: rawValue)
        } else {
            self = .none
        }
    }
    
    public var rawValue: String? {
        return self
    }
}

extension String: RawRepresentable {
    public typealias RawValue = String
    
    public init?(rawValue: String) {
        self.init(rawValue)
    }
    
    public var rawValue: String {
        return self
    }
}

class Settings
{
    static let shared = Settings()
    
    public enum SearchProviderSetting: String, CaseIterable {
        case google         = "Google"
        case duckduckgo     = "DuckDuckGo"
        case searxnor       = "Searx.nor"
        case whoogle        = "Whoogle.nor"
        
        func provider() -> SearchProvider {
            switch self {
                case .google:     return SearchProvider.google
                case .duckduckgo: return SearchProvider.duckduckgo
                case .searxnor:   return SearchProvider.searxnor
                case .whoogle:    return SearchProvider.whoogle
            }
        }
    }
    
    @SettingProperty(key: "searchProvider")
    public var searchProvider: SearchProviderSetting = .searxnor
    
    @SettingProperty(key: "redirectRules")
    public var redirectRules: [String: String] = [:]
    
    func redirectRule(for url: URL) -> URL? {
        if var components = URLComponents(url: url, resolvingAgainstBaseURL: false), var host = url.host {
            // Remove "www." if necessary.
            host = host.replacingOccurrences(of: "www.", with: "")
            
            if let alternateHost = redirectRules[host] {
                components.host = alternateHost
                
                // Since redirected hosts are usually self-hosted.
                // If https is required, it will be upgraded anyway. 
                components.scheme = "http"
                
                return components.url
            }
        }
        
        return nil
    }
    
    @SettingProperty(key: "userScript")
    public var userScript: String = ""
    
    @SettingProperty(key: "userStylesheet")
    public var userStylesheet: String = ""
    
    @SettingProperty(key: "syncServer")
    public var syncServer: Optional<String> = "https://attractor.severnaya.net"
}
