import Foundation
import SwiftUI
import WebKit

#if os(iOS)
import UIKit
#else
import AppKit
#endif

struct RigWebView {
    let peer: PeerController
}

#if os(iOS)
extension RigWebView: UIViewRepresentable {
    func makeCoordinator() -> RigWebCoordinator { RigWebCoordinator(peer: peer) }

    func makeUIView(context: Context) -> WKWebView {
        makeRigWebView(coordinator: context.coordinator, compact: true)
    }

    func updateUIView(_ webView: WKWebView, context: Context) {}

    static func dismantleUIView(_ webView: WKWebView, coordinator: RigWebCoordinator) {
        webView.configuration.userContentController.removeScriptMessageHandler(forName: RigWebCoordinator.bridgeName)
    }
}
#else
extension RigWebView: NSViewRepresentable {
    func makeCoordinator() -> RigWebCoordinator { RigWebCoordinator(peer: peer) }

    func makeNSView(context: Context) -> WKWebView {
        makeRigWebView(coordinator: context.coordinator, compact: false)
    }

    func updateNSView(_ webView: WKWebView, context: Context) {}

    static func dismantleNSView(_ webView: WKWebView, coordinator: RigWebCoordinator) {
        webView.configuration.userContentController.removeScriptMessageHandler(forName: RigWebCoordinator.bridgeName)
    }
}
#endif

private func makeRigWebView(coordinator: RigWebCoordinator, compact: Bool) -> WKWebView {
    let configuration = WKWebViewConfiguration()
    configuration.defaultWebpagePreferences.allowsContentJavaScript = true
    configuration.mediaTypesRequiringUserActionForPlayback = []
    #if os(iOS)
    configuration.allowsInlineMediaPlayback = true
    #endif
    configuration.userContentController.add(coordinator, name: RigWebCoordinator.bridgeName)

    let webView = WKWebView(frame: .zero, configuration: configuration)
    webView.navigationDelegate = coordinator
    webView.uiDelegate = coordinator
    webView.allowsBackForwardNavigationGestures = false
    coordinator.attach(to: webView)

    guard let index = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "Web")
            ?? Bundle.main.url(forResource: "index", withExtension: "html") else {
        webView.loadHTMLString("<h1>Web runtime missing</h1><p>Run sync_web.sh, then regenerate the Xcode project.</p>", baseURL: nil)
        return webView
    }

    var components = URLComponents(url: index, resolvingAgainstBaseURL: false)
    components?.queryItems = [URLQueryItem(name: "compact", value: compact ? "1" : "0")]
    webView.loadFileURL(components?.url ?? index, allowingReadAccessTo: index.deletingLastPathComponent())
    return webView
}

final class RigWebCoordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate {
    static let bridgeName = "anime25d"

    private let peer: PeerController
    private weak var webView: WKWebView?

    init(peer: PeerController) {
        self.peer = peer
        super.init()
        peer.onCommand = { [weak self] command in self?.apply(command) }
        peer.onStatus = { [weak self] label in self?.setStatus(label) }
    }

    func attach(to webView: WKWebView) {
        self.webView = webView
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard message.name == Self.bridgeName,
              let data = jsonData(from: message.body),
              let command = try? JSONDecoder().decode(RigCommand.self, from: data),
              command.isValid else { return }
        peer.send(command)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        setStatus(peer.connectionLabel)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        guard navigationAction.targetFrame?.isMainFrame == true,
              let url = navigationAction.request.url,
              let scheme = url.scheme?.lowercased(),
              !["file", "about"].contains(scheme) else {
            decisionHandler(.allow)
            return
        }

        if navigationAction.navigationType == .linkActivated, ["https", "http"].contains(scheme) {
            #if os(iOS)
            UIApplication.shared.open(url)
            #else
            NSWorkspace.shared.open(url)
            #endif
        }
        decisionHandler(.cancel)
    }

    func webView(_ webView: WKWebView, requestMediaCapturePermissionFor origin: WKSecurityOrigin, initiatedByFrame frame: WKFrameInfo, type: WKMediaCaptureType, decisionHandler: @escaping (WKPermissionDecision) -> Void) {
        decisionHandler(origin.protocol == "file" ? .grant : .deny)
    }

    private func jsonData(from body: Any) -> Data? {
        if let text = body as? String { return text.data(using: .utf8) }
        guard JSONSerialization.isValidJSONObject(body) else { return nil }
        return try? JSONSerialization.data(withJSONObject: body)
    }

    private func apply(_ command: RigCommand) {
        guard let data = try? JSONEncoder().encode(command),
              let json = String(data: data, encoding: .utf8) else { return }
        webView?.evaluateJavaScript("window.Anime25D?.applyCommand(\(json));")
    }

    private func setStatus(_ label: String) {
        guard let data = try? JSONEncoder().encode(label),
              let json = String(data: data, encoding: .utf8) else { return }
        let connected = label.hasPrefix("Connected:")
        webView?.evaluateJavaScript("window.Anime25D?.setPeerStatus(\(json), \(connected));")
    }
}
