//
//  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()

    // Map of search engine name -> URL template containing %q or %s placeholder
    // Defaults preserve the previous built-in engines
    @SettingProperty(key: "searchEngines")
    public var searchEngines: [String: String] = [
        "Google":      "https://google.com/search?q=%q&gbv=1",
        "DuckDuckGo":  "https://html.duckduckgo.com/html/?q=%q",
    ]

    // Name of the default search engine from `searchEngines`
    @SettingProperty(key: "defaultSearchEngine")
    public var defaultSearchEngineName: String = "Searx.nor"

    // Convenience to build a SearchProvider from current default
    func currentSearchProvider() -> SearchProvider {
        if let template = searchEngines[defaultSearchEngineName] {
            return SearchProvider.fromTemplate(template)
        }
        // Fallback to Google if something goes wrong
        return SearchProvider.fromTemplate("https://google.com/search?q=%q&gbv=1")
    }
    
    @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"
}
