//
//  ContentView.swift
//  Block 89
//  
//  Created by Willem L. Middelkoop on 15/02/2026.
//

import SwiftUI
import WebKit
import StoreKit

// MARK: - ContentView

struct ContentView: View {
    var body: some View {
        GameWebView()
            .ignoresSafeArea()
            .statusBarHidden()
    }
}

// MARK: - GameWebView (UIViewRepresentable)

struct GameWebView: UIViewRepresentable {
    
    func makeCoordinator() -> Coordinator {
        Coordinator()
    }
    
    func makeUIView(context: Context) -> WKWebView {
        let config = WKWebViewConfiguration()
        config.userContentController.add(context.coordinator, name: "block89")
        
        // Allow inline media playback (for Web Audio)
        config.allowsInlineMediaPlayback = true
        config.mediaTypesRequiringUserActionForPlayback = []
        
        let webView = WKWebView(frame: .zero, configuration: config)
        webView.scrollView.isScrollEnabled = false
        webView.scrollView.bounces = false
        webView.scrollView.contentInsetAdjustmentBehavior = .never
        webView.isOpaque = true
        webView.backgroundColor = .black
        webView.navigationDelegate = context.coordinator
        webView.uiDelegate = context.coordinator
        
        // Disable magnification gestures
        webView.scrollView.minimumZoomScale = 1.0
        webView.scrollView.maximumZoomScale = 1.0
        
        // Store reference for lifecycle messages
        context.coordinator.webView = webView
        
        // Load the game
        if let url = Bundle.main.url(forResource: "block89", withExtension: "html") {
            webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
        }
        
        // Observe app lifecycle
        context.coordinator.observeLifecycle()
        
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {}
    
    // Clean up message handler to avoid retain cycle
    static func dismantleUIView(_ uiView: WKWebView, coordinator: Coordinator) {
        uiView.configuration.userContentController.removeScriptMessageHandler(forName: "block89")
        coordinator.removeLifecycleObservers()
    }
}

// MARK: - Coordinator (Bridge)

class Coordinator: NSObject, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate {
    
    weak var webView: WKWebView?
    
    private let impactHeavy = UIImpactFeedbackGenerator(style: .heavy)
    private let impactMedium = UIImpactFeedbackGenerator(style: .medium)
    private let impactLight = UIImpactFeedbackGenerator(style: .light)
    
    // MARK: Lifecycle observers
    
    func observeLifecycle() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(appWillResignActive),
            name: UIApplication.willResignActiveNotification,
            object: nil
        )
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(appDidBecomeActive),
            name: UIApplication.didBecomeActiveNotification,
            object: nil
        )
    }
    
    func removeLifecycleObservers() {
        NotificationCenter.default.removeObserver(self)
    }
    
    @objc private func appWillResignActive() {
        sendToJS("app_willResignActive")
    }
    
    @objc private func appDidBecomeActive() {
        sendToJS("app_didBecomeActive")
    }
    
    private func sendToJS(_ message: String) {
        DispatchQueue.main.async { [weak self] in
            self?.webView?.evaluateJavaScript("receiveMessage('\(message)')")
        }
    }
    
    // MARK: WKScriptMessageHandler
    
    func userContentController(
        _ userContentController: WKUserContentController,
        didReceive message: WKScriptMessage
    ) {
        guard let body = message.body as? [String: Any],
              let label = body["l"] as? String,
              let data = body["d"] as? String else { return }
        
        switch label {
        case "vibrate":
            vibrate(duration: Int(data) ?? 0)
        case "share":
            share(data: data)
        case "review":
            requestReview()
        default:
            print("Block 89: unhandled message [\(label)]: \(data)")
        }
    }
    
    // MARK: Vibrate
    
    private func vibrate(duration: Int) {
        if duration > 100 {
            impactHeavy.impactOccurred()
        } else if duration > 10 {
            impactMedium.impactOccurred()
        } else {
            impactLight.impactOccurred()
        }
    }
    
    // MARK: Share (base64 GIF)
    
    private func share(data: String) {
        guard let jsonData = data.data(using: .utf8),
              let shareJSON = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
              let imageBase64 = shareJSON["image"] as? String,
              let text = shareJSON["text"] as? String else { return }
        
        // Strip data URL prefix: "data:image/gif;base64,..."
        let components = imageBase64.components(separatedBy: ",")
        guard components.count == 2,
              let imageData = Data(base64Encoded: components[1], options: .ignoreUnknownCharacters) else { return }
        
        // Determine file extension from MIME type
        let isGif = components[0].contains("gif")
        let ext = isGif ? "gif" : "jpeg"
        
        // Write to temp file so share sheet shows correct file type
        let tempURL = FileManager.default.temporaryDirectory
            .appendingPathComponent("block89-replay.\(ext)")
        
        do {
            try imageData.write(to: tempURL)
        } catch {
            print("Block 89: failed to write temp file: \(error)")
            return
        }
        
        let items: [Any] = [text, tempURL]
        let ac = UIActivityViewController(activityItems: items, applicationActivities: nil)
        
        // Present from the root view controller
        if let scene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let root = scene.windows.first?.rootViewController {
            ac.popoverPresentationController?.sourceView = root.view
            ac.popoverPresentationController?.sourceRect = CGRect(
                x: root.view.bounds.midX, y: root.view.bounds.midY,
                width: 0, height: 0
            )
            ac.popoverPresentationController?.permittedArrowDirections = []
            root.present(ac, animated: true)
        }
    }
    
    // MARK: Review
    
    private func requestReview() {
        if let scene = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene {
            SKStoreReviewController.requestReview(in: scene)
        }
    }
    
    // MARK: WKNavigationDelegate — open external links in Safari
    
    func webView(
        _ webView: WKWebView,
        decidePolicyFor navigationAction: WKNavigationAction,
        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
    ) {
        guard let url = navigationAction.request.url else {
            decisionHandler(.allow)
            return
        }
        
        // Allow local file loads and about:blank
        if url.isFileURL || url.scheme == "about" {
            decisionHandler(.allow)
            return
        }
        
        // External link (http/https) — open in system browser
        if url.scheme == "http" || url.scheme == "https" {
            UIApplication.shared.open(url)
            decisionHandler(.cancel)
            return
        }
        
        decisionHandler(.allow)
    }
    
    // MARK: WKUIDelegate — handle target="_blank" links
    
    func webView(
        _ webView: WKWebView,
        createWebViewWith configuration: WKWebViewConfiguration,
        for navigationAction: WKNavigationAction,
        windowFeatures: WKWindowFeatures
    ) -> WKWebView? {
        // target="_blank" links — open externally
        if let url = navigationAction.request.url,
           url.scheme == "http" || url.scheme == "https" {
            UIApplication.shared.open(url)
        }
        return nil
    }
}

// MARK: - Preview

#Preview {
    ContentView()
}
