import { useState, useEffect, useRef } from "react";
import { Copy, Check } from "lucide-react";
import hljs from "highlight.js/lib/core";
import typescript from "highlight.js/lib/languages/typescript";
import javascript from "highlight.js/lib/languages/javascript";
import python from "highlight.js/lib/languages/python";
import css from "highlight.js/lib/languages/css";
import json from "highlight.js/lib/languages/json";
import xml from "highlight.js/lib/languages/xml";
import bash from "highlight.js/lib/languages/bash";
import sql from "highlight.js/lib/languages/sql";
import go from "highlight.js/lib/languages/go";
import rust from "highlight.js/lib/languages/rust";
import java from "highlight.js/lib/languages/java";
import cpp from "highlight.js/lib/languages/cpp";
import markdownLang from "highlight.js/lib/languages/markdown";
import yaml from "highlight.js/lib/languages/yaml";
import { Button } from "./ui/button";
import { ChatFileCard } from "./ChatFileCard";
import type { ChatFile } from "@paperclipai/shared";

// Register languages with hljs
hljs.registerLanguage("typescript", typescript);
hljs.registerLanguage("javascript", javascript);
hljs.registerLanguage("python", python);
hljs.registerLanguage("css", css);
hljs.registerLanguage("json", json);
hljs.registerLanguage("xml", xml);
hljs.registerLanguage("bash", bash);
hljs.registerLanguage("sql", sql);
hljs.registerLanguage("go", go);
hljs.registerLanguage("rust", rust);
hljs.registerLanguage("java", java);
hljs.registerLanguage("cpp", cpp);
hljs.registerLanguage("markdown", markdownLang);
hljs.registerLanguage("yaml", yaml);

function extToLang(filename: string): string {
  const ext = filename.split(".").pop()?.toLowerCase() ?? "";
  const map: Record<string, string> = {
    ts: "typescript", tsx: "typescript", js: "javascript", jsx: "javascript",
    py: "python", rb: "ruby", rs: "rust", go: "go", java: "java",
    css: "css", html: "xml", json: "json", sh: "bash", bash: "bash",
    yaml: "yaml", yml: "yaml", toml: "ini", md: "markdown",
    c: "c", cpp: "cpp", cs: "csharp", kt: "kotlin", swift: "swift",
    php: "php", sql: "sql", xml: "xml",
  };
  return map[ext] ?? ext;
}

/**
 * Safely renders hljs-highlighted HTML into a DOM node.
 * hljs.highlight() produces only <span class="hljs-*"> tokens from source code.
 * We parse through DOMParser (sandboxed document) and transfer child nodes
 * rather than assigning raw HTML strings, so no script execution is possible.
 */
function applyHighlightedHtml(el: HTMLElement, highlightedHtml: string): void {
  const parser = new DOMParser();
  const doc = parser.parseFromString(
    `<code>${highlightedHtml}</code>`,
    "text/html"
  );
  const sourceCode = doc.body.firstChild;
  if (!sourceCode) return;
  // Transfer parsed child nodes into the target element
  el.replaceChildren(...Array.from(sourceCode.childNodes));
}

interface ChatCodeFilePreviewProps {
  file: ChatFile;
  contentPath: string;
}

export function ChatCodeFilePreview({ file, contentPath }: ChatCodeFilePreviewProps) {
  const [content, setContent] = useState<string | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const [copied, setCopied] = useState(false);
  const codeRef = useRef<HTMLElement>(null);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(false);
    setContent(null);

    fetch(contentPath, { credentials: "include" })
      .then((res) => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return res.text();
      })
      .then((text) => {
        if (cancelled) return;
        const capped =
          text.length > 50000 ? text.slice(0, 50000) + "\n// ... truncated" : text;
        setContent(capped);
        setLoading(false);
      })
      .catch(() => {
        if (cancelled) return;
        setError(true);
        setLoading(false);
      });

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

  // Apply highlighting whenever content or code element changes
  useEffect(() => {
    if (!codeRef.current || content === null) return;

    const lang = extToLang(file.originalFilename);
    let highlighted: string;
    try {
      const registeredLangs = hljs.listLanguages();
      if (registeredLangs.includes(lang)) {
        highlighted = hljs.highlight(content, { language: lang }).value;
      } else {
        highlighted = hljs.highlightAuto(content).value;
      }
    } catch {
      highlighted = hljs.highlightAuto(content).value;
    }

    applyHighlightedHtml(codeRef.current, highlighted);
  }, [content, file.originalFilename]);

  if (loading) {
    return <div className="rounded-lg border border-border bg-muted animate-pulse h-[120px]" />;
  }

  if (error || content === null) {
    return <ChatFileCard file={file} contentPath={contentPath} />;
  }

  const lang = extToLang(file.originalFilename);

  function handleCopy() {
    navigator.clipboard.writeText(content ?? "").catch(() => {
      // Ignore clipboard errors in restricted environments
    });
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  }

  return (
    <div className="flex flex-col gap-1">
      <div className="paperclip-markdown rounded-lg border border-border overflow-hidden">
        <div className="flex items-center justify-between bg-card border-b border-border px-3 py-1">
          <span className="text-xs text-muted-foreground font-mono">{lang || "text"}</span>
          <Button
            variant="ghost"
            size="icon"
            className="h-6 w-6"
            aria-label={copied ? "Copied!" : "Copy code"}
            onClick={handleCopy}
          >
            {copied ? (
              <Check className="h-3.5 w-3.5" />
            ) : (
              <Copy className="h-3.5 w-3.5" />
            )}
          </Button>
        </div>
        <div className="max-h-[400px] overflow-auto">
          <pre className="m-0 p-4">
            <code ref={codeRef} />
          </pre>
        </div>
      </div>
      <ChatFileCard file={file} contentPath={contentPath} />
    </div>
  );
}
