//
//  Tab.swift
//  SBrowser
//
//  Created by James Magahern on 7/29/20.
//

import UIKit
import Combine

protocol TabDelegate: AnyObject
{
    func didBlockScriptOrigin(_ origin: String, forTab: Tab)
}

class Tab: NSObject, SBRProcessBundleBridgeDelegate
{
    public weak var delegate: TabDelegate?
    
    public var tabInfo: TabInfo {
        get {
            updateMetadata()
            return _tabInfo
        }
    }
    
    public let homeURL: URL?
    public let bridge: ProcessBundleBridge
    public var webView: WKWebView {
        if self.loadedWebView == nil {
            self.loadedWebView = bridge.webView
            
            if let homeURL = homeURL {
                beginLoadingURL(homeURL)
            }
        }
        
        return bridge.webView
    }
    public var policyManager: ResourcePolicyManager
    
    private var _tabInfo: TabInfo = TabInfo()
    
    private var loadedWebView: WKWebView? = nil
    public var title: String? { get { tabInfo.title } }
    public var url: URL? {
        get {
            if let urlString = tabInfo.urlString {
                return URL(string: urlString)
            }
            
            return nil
        }
    }
    
    public var javaScriptEnabled: Bool = false {
        didSet { bridge.allowAllScripts = javaScriptEnabled }
    }
    
    public lazy var colorTheme: [UIColor] = {
        // Looks really cool as-is, but maybe compute a color based on the
        // title or URL to make it show up the same for each website? 
        return [
            UIColor(
                hue: CGFloat.random(in: 0...1.0),
                saturation: 0.89,
                brightness: 0.3,
                alpha: 1.0
            ),
            UIColor(
                hue: CGFloat.random(in: 0...1.0),
                saturation: 1.0,
                brightness: 0.7,
                alpha: 1.0
            ),
        ]
    }()
    
    public var identifier = UUID()
    @Published public var favicon: UIImage?
    
    public var loadError: Error?
    public var contentProcessTerminated: Bool = false 
    
    private var faviconHost: String?
    private var faviconRequest: AnyCancellable?
    
    public var allowedScriptOrigins = Set<String>()
    public var blockedScriptOrigins = Set<String>()
    
    public var titleObservation: NSKeyValueObservation?
    public var urlObservation: NSKeyValueObservation?
    public var faviconObservation: AnyCancellable?
    
    convenience init(policyManager: ResourcePolicyManager) {
        self.init(url: nil, policyManager: policyManager, webViewConfiguration: nil)
    }
    
    convenience init(urlString: String, policyManager: ResourcePolicyManager) {
        self.init(url: URL(string: urlString), policyManager: policyManager, webViewConfiguration: nil)
    }
    
    init(url: URL?, policyManager: ResourcePolicyManager, webViewConfiguration: WKWebViewConfiguration?) {
        self.homeURL = url
        self.policyManager = policyManager
        
        self.bridge = ProcessBundleBridge(webViewConfiguration: webViewConfiguration)
        self.bridge.policyDataSource = policyManager
        
        super.init()
        
        bridge.delegate = self
    }
    
    deinit {
        bridge.tearDown()
    }
    
    func beginLoadingURL(_ url: URL) {
        let request = URLRequest(url: url)
        webView.load(request)
    }
    
    // MARK: SBRProcessBundleBridgeDelegate
    
    func webProcess(_ bridge: ProcessBundleBridge, didAllowScriptResourceFromOrigin origin: String) {
        print("Allowed script resource from origin: \(origin)")
        allowedScriptOrigins.formUnion([ origin ])
    }
    
    func webProcess(_ bridge: ProcessBundleBridge, didBlockScriptResourceFromOrigin origin: String) {
        print("Blocked script resource from origin: \(origin)")
        blockedScriptOrigins.formUnion([ origin ])
        delegate?.didBlockScriptOrigin(origin, forTab: self)
    }
    
    func updateFaviconForURL(_ url: URL) {
        if let faviconHost = faviconHost, url.host == faviconHost {} else {
            guard var faviconURLComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
            faviconURLComponents.path = "/favicon.ico"
                
            let defaultImage = UIImage(systemName: "globe")
            guard let faviconURL = faviconURLComponents.url else { return }
            faviconRequest = URLSession.shared.dataTaskPublisher(for: faviconURL)
                .map { (data: Data, response: URLResponse) -> UIImage? in
                    UIImage(data: data)
                }
                .replaceError(with: defaultImage)
                .replaceNil(with: defaultImage)
                .assign(to: \.favicon, on: self)
        }
    }
    
    private func updateMetadata() {
        guard contentProcessTerminated == false else { return }
        
        _tabInfo = TabInfo(
            title: loadedWebView?.title,
            urlString: loadedWebView?.url?.absoluteString ?? self.homeURL?.absoluteString,
            faviconData: self.favicon?.pngData(),
            identifier: self.identifier
        )
    }
}
