import SwiftUI
import WebKit
import Combine

/// SwiftUI wrapper around WKWebView. Exposes reload / navigation
/// commands through a Coordinator that holds the WKWebView reference.
struct WebView: NSViewRepresentable {

    let url: URL
    @Binding var canGoBack: Bool
    @Binding var canGoForward: Bool
    @Binding var isLoading: Bool
    @Binding var pageTitle: String
    let developerExtras: Bool

    /// Token broadcast by `App` commands to trigger navigation actions.
    let action: PassthroughSubject<WebAction, Never>

    func makeCoordinator() -> Coordinator { Coordinator(self) }

    func makeNSView(context: Context) -> WKWebView {
        let cfg = WKWebViewConfiguration()
        cfg.websiteDataStore = .nonPersistent()
        if developerExtras {
            cfg.preferences.setValue(true, forKey: "developerExtrasEnabled")
        }
        let wv = WKWebView(frame: .zero, configuration: cfg)
        wv.navigationDelegate = context.coordinator
        wv.uiDelegate = context.coordinator
        wv.allowsBackForwardNavigationGestures = true
        wv.allowsMagnification = true
        wv.load(URLRequest(url: url))

        context.coordinator.bind(webView: wv)
        return wv
    }

    func updateNSView(_ wv: WKWebView, context: Context) {
        // Reload if the URL prop changed (e.g. user edited it in Settings).
        if wv.url?.absoluteString != url.absoluteString {
            wv.load(URLRequest(url: url))
        }
    }

    enum WebAction { case reload, back, forward, home, openInBrowser, copyURL }

    // -------------------------------------------------------------------------
    final class Coordinator: NSObject, WKNavigationDelegate, WKUIDelegate {
        var parent: WebView
        weak var webView: WKWebView?
        private var cancellables = Set<AnyCancellable>()
        private var observers: [NSKeyValueObservation] = []

        init(_ p: WebView) { parent = p }

        func bind(webView: WKWebView) {
            self.webView = webView
            observers.append(webView.observe(\.canGoBack)    { [weak self] wv, _ in
                Task { @MainActor in self?.parent.canGoBack = wv.canGoBack }
            })
            observers.append(webView.observe(\.canGoForward) { [weak self] wv, _ in
                Task { @MainActor in self?.parent.canGoForward = wv.canGoForward }
            })
            observers.append(webView.observe(\.isLoading)    { [weak self] wv, _ in
                Task { @MainActor in self?.parent.isLoading = wv.isLoading }
            })
            observers.append(webView.observe(\.title)        { [weak self] wv, _ in
                Task { @MainActor in self?.parent.pageTitle = wv.title ?? "" }
            })

            parent.action
                .receive(on: DispatchQueue.main)
                .sink { [weak self] act in self?.handle(act) }
                .store(in: &cancellables)
        }

        private func handle(_ a: WebAction) {
            guard let wv = webView else { return }
            switch a {
            case .reload:        wv.reload()
            case .back:          if wv.canGoBack    { wv.goBack() }
            case .forward:       if wv.canGoForward { wv.goForward() }
            case .home:          wv.load(URLRequest(url: parent.url))
            case .openInBrowser: if let u = wv.url { NSWorkspace.shared.open(u) }
            case .copyURL:
                if let u = wv.url?.absoluteString {
                    let pb = NSPasteboard.general
                    pb.clearContents()
                    pb.setString(u, forType: .string)
                }
            }
        }
    }
}
