import SwiftUI
import WebKit

// MARK: - Shared Article Renderer

@MainActor
final class ArticleRenderer {
    static let shared = ArticleRenderer()

    let webView: WKWebView

    private init() {
        let config = WKWebViewConfiguration()
        config.allowsInlineMediaPlayback = true

        webView = WKWebView(frame: .zero, configuration: config)
        webView.isOpaque = false
        webView.backgroundColor = .clear
        webView.scrollView.isScrollEnabled = true

        // Pre-warm with realistic content. An empty <html><body></body></html>
        // only warms the WebContent process. Loading the real CSS template with
        // sample text forces WebKit to also parse CSS, load/measure system fonts,
        // initialize the layout engine, and spin up the compositing pipeline.
        // This shifts the ~3s first-render cost to app launch (background) so the
        // first article open doesn't stall.
        webView.loadHTMLString(ArticleHTMLBuilder.build(
            title: "Warming up",
            url: nil,
            feedName: "Reader",
            author: "System",
            timeAgo: "now",
            readingTime: "1 min",
            body: """
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. \
            Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
            <h2>Section heading</h2>
            <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.</p>
            <blockquote>Quoted text for blockquote styling.</blockquote>
            <pre><code>code { display: block; }</code></pre>
            """
        ), baseURL: nil)
    }
}

// MARK: - Article Web View

struct ArticleWebView: UIViewRepresentable {
    let html: String

    func makeUIView(context: Context) -> UIView {
        let container = UIView()
        container.backgroundColor = .clear

        let webView = ArticleRenderer.shared.webView
        webView.removeFromSuperview()
        webView.frame = container.bounds
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        container.addSubview(webView)

        webView.navigationDelegate = context.coordinator
        return container
    }

    func updateUIView(_ container: UIView, context: Context) {
        let webView = ArticleRenderer.shared.webView
        webView.navigationDelegate = context.coordinator

        let newHTML = html

        // Only reload if content meaningfully changed
        guard context.coordinator.lastHTML != newHTML else { return }

        let isUpgrade = context.coordinator.lastHTML != nil
        context.coordinator.lastHTML = newHTML

        if isUpgrade {
            // Content upgrade (partial → full): swap only #article-body contents.
            // Header + outer document structure stay intact. Scroll preserved.
            let escapedContent = escapeForJS(extractArticleBody(from: newHTML))
            let js = """
            (function() {
                var el = document.getElementById('article-body');
                if (!el) return 'no-container';
                var scrollY = window.scrollY;
                el.innerHTML = \(escapedContent);
                window.scrollTo(0, scrollY);
                return 'ok';
            })();
            """
            webView.evaluateJavaScript(js) { result, error in
                // Fall back to full reload if container missing or JS error
                let status = result as? String
                if error != nil || status != "ok" {
                    webView.loadHTMLString(newHTML, baseURL: nil)
                }
            }
        } else {
            // First load — full page load
            webView.loadHTMLString(newHTML, baseURL: nil)
        }
    }

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

    /// Extract content inside <div id="article-body">...</div>
    private func extractArticleBody(from html: String) -> String {
        let marker = "<div id=\"article-body\">"
        guard let start = html.range(of: marker) else { return "" }
        let afterMarker = html[start.upperBound...]
        // Find the matching </div> before </body>
        guard let bodyEnd = afterMarker.range(of: "</div>\n        </body>")
                ?? afterMarker.range(of: "</div></body>")
                ?? afterMarker.range(of: "</div>\n</body>") else {
            // Fallback: take everything up to </body>
            if let end = afterMarker.range(of: "</body>") {
                return String(afterMarker[..<end.lowerBound])
            }
            return String(afterMarker)
        }
        return String(afterMarker[..<bodyEnd.lowerBound])
    }

    /// Escape a string for use as a JavaScript string literal
    private func escapeForJS(_ str: String) -> String {
        let escaped = str
            .replacingOccurrences(of: "\\", with: "\\\\")
            .replacingOccurrences(of: "'", with: "\\'")
            .replacingOccurrences(of: "\n", with: "\\n")
            .replacingOccurrences(of: "\r", with: "")
        return "'\(escaped)'"
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        var lastHTML: String?

        func webView(
            _ webView: WKWebView,
            decidePolicyFor navigationAction: WKNavigationAction,
            decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
        ) {
            if navigationAction.navigationType == .linkActivated,
               let url = navigationAction.request.url {
                UIApplication.shared.open(url)
                decisionHandler(.cancel)
                return
            }
            decisionHandler(.allow)
        }
    }
}
