import React, { useEffect, useRef, useState } from "react";
import * as esbuild from "esbuild-wasm";
import { IFileEntry } from "@fullstackcraftllc/codevideo-types";

export interface WebPreviewProps {
  /** Files that make up the virtual project */
  files: IFileEntry[];
  /** Height of the preview (use CSS units). Defaults to 400px. */
  height?: string | number;
}

// Multiple WASM URLs as fallbacks for esbuild-wasm 0.25.5
const WASM_URLS = [
  "https://unpkg.com/esbuild-wasm@0.25.5/esbuild.wasm",
  "https://cdn.jsdelivr.net/npm/esbuild-wasm@0.25.5/esbuild.wasm"
];
// CDN versions for React / ReactDOM if TSX is used & marked external
const REACT_CDN =
  "https://esm.sh/react@18?bundle";
const REACT_DOM_CDN =
  "https://esm.sh/react-dom@18?bundle";

// Global singleton to ensure esbuild is only initialized once
let esbuildInitialized = false;
let esbuildInitializing = false;
let esbuildInitPromise: Promise<void> | null = null;

// Helper function to try initializing esbuild with different WASM URLs
const initializeEsbuild = async (): Promise<void> => {
  // If already initialized, return immediately
  if (esbuildInitialized) {
    return;
  }

  // If currently initializing, wait for the existing promise
  if (esbuildInitializing && esbuildInitPromise) {
    return esbuildInitPromise;
  }

  // Mark as initializing and create the promise
  esbuildInitializing = true;
  esbuildInitPromise = (async () => {
    let lastError: Error | null = null;
    
    for (const wasmURL of WASM_URLS) {
      try {
        await esbuild.initialize({ 
          wasmURL, 
          worker: false // Use same-thread to avoid worker issues
        });
        console.log(`Successfully initialized esbuild with ${wasmURL}`);
        esbuildInitialized = true;
        esbuildInitializing = false;
        return;
      } catch (error) {
        console.warn(`Failed to initialize esbuild with ${wasmURL}:`, error);
        lastError = error instanceof Error ? error : new Error(String(error));
      }
    }
    
    esbuildInitializing = false;
    throw lastError || new Error('Failed to initialize esbuild with any WASM URL');
  })();

  return esbuildInitPromise;
};

// Virtual‑file plugin – lets esbuild read from an in‑memory map
const createVirtualPlugin = (
  vfs: Map<string, string>
): esbuild.Plugin => ({
  name: "virtual-fs",
  setup(build) {
    // Resolve any import path that exists in the virtual map
    build.onResolve({ filter: /.*/ }, (args) => {
      // Ignore externalised deps (react, etc.)
      if (args.path === "react" || args.path === "react-dom") {
        return { path: args.path, external: true };
      }

      // Convert any bare/relative import into an absolute posix path
      const path = args.path.startsWith("/")
        ? args.path
        : `/${args.path.replace(/^\.\//, "")}`;

      if (vfs.has(path)) {
        return { path, namespace: "vfs" };
      }
      return undefined; // let esbuild handle/throw
    });

    // Serve file contents from the map
    build.onLoad({ filter: /.*/, namespace: "vfs" }, async (args) => {
      const contents = vfs.get(args.path) ?? "";
      const ext = args.path.split(".").pop() ?? "js";
      const loader = ((): esbuild.Loader => {
        switch (ext) {
          case "ts":
            return "ts";
          case "tsx":
            return "tsx";
          case "jsx":
            return "jsx";
          default:
            return "js";
        }
      })();

      return { contents, loader };
    });
  },
});

export const WebPreview: React.FC<WebPreviewProps> = ({
  files,
  height = "100vh", // Default to full height instead of 400px for better compatibility
}) => {
  const iframeRef = useRef<HTMLIFrameElement>(null);
  const [esbuildReady, setEsbuildReady] = useState(esbuildInitialized);
  const [esbuildError, setEsbuildError] = useState<string | null>(null);

  // Initialise esbuild on first mount using singleton pattern
  useEffect(() => {
    if (esbuildInitialized) {
      setEsbuildReady(true);
      return;
    }

    let isMounted = true;
    
    (async () => {
      try {
        await initializeEsbuild();
        
        if (isMounted) {
          setEsbuildReady(true);
          setEsbuildError(null);
        }
      } catch (error) {
        console.error('Failed to initialize esbuild:', error);
        if (isMounted) {
          setEsbuildError(error instanceof Error ? error.message : 'Unknown error');
        }
      }
    })();

    return () => {
      isMounted = false;
    };
  }, []);

  // Re‑bundle whenever files or esbuild are ready
  useEffect(() => {
    if (!esbuildReady) return;

    (async () => {
      try {
        // Build a quick lookup map → path **must** start with "/"
        const vfs = new Map<string, string>();
        for (const f of files) {
          const normalized = f.path.startsWith("/")
            ? f.path
            : `/${f.path}`;
          vfs.set(normalized, f.content);
        }

        // Detect entry HTML (first .html file) & JS entry (first .ts/tsx/js/jsx)
        const entryHtml = files.find((f) => f.path.endsWith(".html"));
        const scriptEntry =
          files.find((f) => /\.(tsx?|jsx?)$/.test(f.path))?.path ??
          "index.tsx";

        // Bundle JS/TS
        let bundledCode = "";
        if (vfs.has(`/${scriptEntry}`)) {
          const result = await esbuild.build({
            bundle: true,
            write: false,
            format: "iife",
            platform: "browser",
            entryPoints: [`/${scriptEntry}`],
            plugins: [createVirtualPlugin(vfs)],
            external: ["react", "react-dom"],
            target: ["es2020"],
            define: {
              'process.env.NODE_ENV': '"development"'
            }
          });
          bundledCode = result.outputFiles[0].text;
        }

        // Compose HTML: inline CSS, add external script tags, then inline bundle
        let html = entryHtml?.content ||
          `<!DOCTYPE html><html><head></head><body></body></html>`;

        // Inline all .css files
        for (const f of files.filter((f) => f.path.endsWith(".css"))) {
          html = html.replace(
            /<\/head>/i,
            `<style>\n${f.content}\n</style></head>`
          );
        }

        // Add React CDN if necessary
        if (bundledCode.includes("React.createElement") || /react/i.test(bundledCode)) {
          html = html.replace(
            /<\/head>/i,
            `<script src="${REACT_CDN}"></script><script src="${REACT_DOM_CDN}"></script></head>`
          );
        }

        // Inline the compiled JS right before </body>
        html = html.replace(
          /<\/body>/i,
          `<script>\n${bundledCode}\n</script></body>`
        );

        // Write result to iframe as srcdoc
        if (iframeRef.current) {
          iframeRef.current.srcdoc = html;
        }
      } catch (error) {
        console.error('Bundle error:', error);
        // Show error in iframe
        if (iframeRef.current) {
          iframeRef.current.srcdoc = `
            <!DOCTYPE html>
            <html>
              <head><title>Bundle Error</title></head>
              <body style="font-family: monospace; padding: 20px; color: #e74c3c;">
                <h3>Bundle Error:</h3>
                <pre>${error instanceof Error ? error.message : 'Unknown error'}</pre>
              </body>
            </html>
          `;
        }
      }
    })();
  }, [files, esbuildReady]);

  // Show error state if esbuild failed to initialize
  if (esbuildError) {
    return (
      <div 
        style={{ 
          width: "100%", 
          height: typeof height === "number" ? `${height}px` : height, 
          border: "1px solid #e74c3c",
          backgroundColor: "#fdf2f2",
          padding: "20px",
          fontFamily: "monospace",
          color: "#e74c3c"
        }}
      >
        <h3>WebPreview Error:</h3>
        <p>Failed to initialize esbuild-wasm:</p>
        <pre>{esbuildError}</pre>
        <p style={{ marginTop: "10px", fontSize: "12px" }}>
          This might be due to browser compatibility issues or network problems loading the WebAssembly module.
        </p>
      </div>
    );
  }

  // Show loading state
  if (!esbuildReady) {
    return (
      <div 
        style={{ 
          width: "100%", 
          height: typeof height === "number" ? `${height}px` : height, 
          border: "1px solid #ccc",
          backgroundColor: "#f9f9f9",
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          fontFamily: "sans-serif",
          color: "#666"
        }}
      >
        Initializing WebPreview...
      </div>
    );
  }

  return (
    <div style={{ width: '100%', height: '100vh' }} data-testid="web-preview">
      <iframe
        ref={iframeRef}
        style={{ width: "100%", height: "100%", border: "0" }}
        sandbox="allow-scripts allow-same-origin"
        title="Web Preview"
      />
    </div>
  );
};