"use client";

import { useEffect, useRef, useState } from "react";

/**
 * Inline loader for a static app (SVGnest) placed under /public/svgnest.
 * It fetches index.html, injects the HTML into a container, then
 * re-executes <script> tags (both external and inline) in order.
 * CSS <link> files are fetched and inlined into <style> tags to avoid path issues.
 *
 * NOTE: This runs in the light DOM (not shadow). That maximizes compatibility
 * with older scripts that rely on document-level selectors. If we see style bleed,
 * we can optionally switch to a Shadow DOM variant later.
 */
export default function SVGNestPanel() {
    const containerRef = useRef<HTMLDivElement | null>(null);
    const [error, setError] = useState<string | null>(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        let cancelled = false;

        async function load() {
            try {
                setLoading(true);
                // 1) Fetch index.html
                const htmlRes = await fetch("/svgnest/index.html", { cache: "no-cache" });
                if (!htmlRes.ok) throw new Error(`Failed to fetch index.html (HTTP ${htmlRes.status})`);
                const htmlText = await htmlRes.text();

                if (cancelled) return;

                // 2) Parse
                const parser = new DOMParser();
                const doc = parser.parseFromString(htmlText, "text/html");

                // 3) Find and inline CSS (convert <link rel="stylesheet"> to <style>)
                const linkEls = Array.from(doc.querySelectorAll<HTMLLinkElement>('link[rel="stylesheet"][href]'));
                for (const link of linkEls) {
                    const href = link.getAttribute("href") || "";
                    const absHref = absolutizeAsset(href);
                    try {
                        const cssRes = await fetch(absHref, { cache: "no-cache" });
                        if (cssRes.ok) {
                            const cssText = await cssRes.text();
                            const style = doc.createElement("style");
                            style.setAttribute("data-inlined-from", href);
                            style.textContent = cssText;
                            link.replaceWith(style);
                        } else {
                            // leave the original link as a fallback
                        }
                    } catch {
                        // leave the original link as a fallback
                    }
                }

                // 4) Extract scripts (to re-execute later) & remove them from doc body
                const scriptInfos: { src?: string; inline?: string; type?: string; nomodule?: boolean; async?: boolean; defer?: boolean }[] = [];
                doc.querySelectorAll("script").forEach((s) => {
                    scriptInfos.push({
                        src: s.getAttribute("src") || undefined,
                                     inline: s.textContent || undefined,
                                     type: s.getAttribute("type") || undefined,
                                     nomodule: s.hasAttribute("nomodule"),
                                     async: s.hasAttribute("async"),
                                     defer: s.hasAttribute("defer"),
                    });
                    s.remove(); // prevent duplicate execution when we inject innerHTML
                });

                // 5) Inject BODY HTML into our container
                const host = containerRef.current!;
                host.innerHTML = doc.body.innerHTML;

                // 6) Sequentially (re)execute scripts in original order
                //    We do this after body is in place so DOM targets exist.
                for (const info of scriptInfos) {
                    await runScript(host, info);
                }
            } catch (e: any) {
                if (!cancelled) setError(e?.message || String(e));
            } finally {
                if (!cancelled) setLoading(false);
            }
        }

        load();
        return () => { cancelled = true; };
    }, []);

    return (
        <div className="w-full">
        {error && (
            <div className="mb-3 rounded border border-rose-600/40 bg-rose-600/10 p-3 text-sm">
            Couldn’t load SVGnest: {error}
            </div>
        )}
        {loading && (
            <div className="mb-3 text-sm text-zinc-400">Loading SVGnest…</div>
        )}
        <div ref={containerRef} className="w-full min-h-[70vh]" />
        </div>
    );
}

/** Convert a (possibly relative) asset path from /svgnest/index.html into an absolute path */
function absolutizeAsset(path: string): string {
    // Ignore full URLs
    try {
        const u = new URL(path);
        return u.toString();
    } catch {
        // not a full URL
    }

    // Handle protocol-relative //host/path -> treat as absolute
    if (path.startsWith("//")) return window.location.protocol + path;

        // If already absolute (/x/y), leave as-is
        if (path.startsWith("/")) return path;

        // Otherwise treat as relative to /svgnest/
        return `/svgnest/${path.replace(/^\.?\//, "")}`;
}

/** Append a new <script> to the container, respecting order. Supports src and inline. */
function runScript(container: HTMLElement, info: { src?: string; inline?: string; type?: string; nomodule?: boolean; async?: boolean; defer?: boolean; }): Promise<void> {
    return new Promise((resolve, reject) => {
        const s = document.createElement("script");
        if (info.type) s.type = info.type;
        if (info.nomodule) s.noModule = true;
        // Preserve async/defer semantics if they existed
        if (info.async) s.async = true;
        if (info.defer) s.defer = true;

        if (info.src) {
            s.src = absolutizeAsset(info.src);
            s.onload = () => resolve();
            s.onerror = () => reject(new Error(`Failed to load script: ${s.src}`));
            container.appendChild(s);
        } else {
            // Inline script: execute synchronously
            s.textContent = info.inline || "";
            container.appendChild(s);
            resolve();
        }
    });
}
