"use client";
import * as React from 'react';
import { cva } from 'class-variance-authority';
import { GitCompare, Plus, Minus, Square, SplitSquareHorizontal, Check, Copy } from 'lucide-react';
import { codeToHtml } from 'shiki';
import { cn } from '@hanzo/ui/lib/utils';
import { Badge } from '@hanzo/ui/badge';
import { Button } from '@hanzo/ui/button';
import { Skeleton } from '@hanzo/ui/skeleton';
import { ToggleGroup, ToggleGroupItem } from '@hanzo/ui/toggle-group';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';

var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var codeDiffVariants = cva(
  "relative overflow-hidden rounded-lg border bg-muted/50",
  {
    variants: {
      theme: {
        dark: "bg-slate-950 border-slate-800",
        light: "bg-slate-50 border-slate-200",
        github: "bg-white border-slate-200",
        "github-dark": "bg-slate-900 border-slate-700"
      },
      size: {
        sm: "text-xs",
        default: "text-sm",
        lg: "text-base"
      }
    },
    defaultVariants: {
      theme: "dark",
      size: "default"
    }
  }
);
var DiffLine = React.memo(
  ({
    change,
    showLineNumbers,
    isUnified = false,
    highlightedContent
  }) => {
    const getLineClasses = /* @__PURE__ */ __name(() => {
      const base = "group relative flex min-h-[1.5rem] items-center py-0.5";
      if (isUnified) {
        switch (change.type) {
          case "add":
            return cn(base, "bg-green-500/10 border-l-2 border-l-green-500");
          case "remove":
            return cn(base, "bg-red-500/10 border-l-2 border-l-red-500");
          default:
            return cn(base, "hover:bg-muted/50");
        }
      }
      return cn(base, "hover:bg-muted/50");
    }, "getLineClasses");
    const getLinePrefix = /* @__PURE__ */ __name(() => {
      if (!isUnified) return null;
      switch (change.type) {
        case "add":
          return /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3 text-green-500 mr-2 flex-shrink-0" });
        case "remove":
          return /* @__PURE__ */ jsx(Minus, { className: "h-3 w-3 text-red-500 mr-2 flex-shrink-0" });
        default:
          return /* @__PURE__ */ jsx("span", { className: "w-3 mr-2 flex-shrink-0" });
      }
    }, "getLinePrefix");
    return /* @__PURE__ */ jsx("div", { className: getLineClasses(), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center px-4 w-full", children: [
      showLineNumbers && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 mr-4", children: !isUnified ? /* @__PURE__ */ jsxs(Fragment, { children: [
        /* @__PURE__ */ jsx("span", { className: "inline-block w-8 select-none text-right text-xs text-muted-foreground", children: change.oldLineNumber || "" }),
        /* @__PURE__ */ jsx("span", { className: "inline-block w-8 select-none text-right text-xs text-muted-foreground", children: change.newLineNumber || "" })
      ] }) : /* @__PURE__ */ jsx("span", { className: "inline-block w-8 select-none text-right text-xs text-muted-foreground", children: change.lineNumber }) }),
      getLinePrefix(),
      /* @__PURE__ */ jsx(
        "div",
        {
          className: "flex-1 overflow-x-auto font-mono",
          dangerouslySetInnerHTML: {
            __html: highlightedContent || change.content
          }
        }
      )
    ] }) });
  }
);
DiffLine.displayName = "DiffLine";
function generateDiff(oldCode, newCode) {
  const oldLines = oldCode.split("\n");
  const newLines = newCode.split("\n");
  const changes = [];
  let oldIndex = 0;
  let newIndex = 0;
  let lineNumber = 1;
  while (oldIndex < oldLines.length || newIndex < newLines.length) {
    const oldLine = oldLines[oldIndex];
    const newLine = newLines[newIndex];
    if (oldIndex >= oldLines.length) {
      changes.push({
        type: "add",
        lineNumber: lineNumber++,
        content: newLine,
        newLineNumber: newIndex + 1
      });
      newIndex++;
    } else if (newIndex >= newLines.length) {
      changes.push({
        type: "remove",
        lineNumber: lineNumber++,
        content: oldLine,
        oldLineNumber: oldIndex + 1
      });
      oldIndex++;
    } else if (oldLine === newLine) {
      changes.push({
        type: "unchanged",
        lineNumber: lineNumber++,
        content: oldLine,
        oldLineNumber: oldIndex + 1,
        newLineNumber: newIndex + 1
      });
      oldIndex++;
      newIndex++;
    } else {
      changes.push({
        type: "remove",
        lineNumber: lineNumber++,
        content: oldLine,
        oldLineNumber: oldIndex + 1
      });
      changes.push({
        type: "add",
        lineNumber: lineNumber++,
        content: newLine,
        newLineNumber: newIndex + 1
      });
      oldIndex++;
      newIndex++;
    }
  }
  return changes;
}
__name(generateDiff, "generateDiff");
var CodeDiff = React.forwardRef(
  ({
    className,
    theme,
    size,
    oldCode,
    newCode,
    language = "text",
    filename,
    oldFilename,
    newFilename,
    showLineNumbers = true,
    showCopyButton = true,
    maxHeight = "500px",
    defaultView = "unified",
    collapseUnchanged = false,
    contextLines = 3,
    ...props
  }, ref) => {
    const [view, setView] = React.useState(defaultView);
    const [copied, setCopied] = React.useState(false);
    const [isLoading, setIsLoading] = React.useState(true);
    const [highlightedOldCode, setHighlightedOldCode] = React.useState("");
    const [highlightedNewCode, setHighlightedNewCode] = React.useState("");
    const [changes, setChanges] = React.useState([]);
    const themeMap = {
      dark: "github-dark",
      light: "github-light",
      github: "github-light",
      "github-dark": "github-dark"
    };
    React.useEffect(() => {
      const highlightCode = /* @__PURE__ */ __name(async () => {
        setIsLoading(true);
        try {
          const [oldHighlighted, newHighlighted] = await Promise.all([
            codeToHtml(oldCode, {
              lang: language,
              theme: themeMap[theme || "dark"]
            }),
            codeToHtml(newCode, {
              lang: language,
              theme: themeMap[theme || "dark"]
            })
          ]);
          const extractContent = /* @__PURE__ */ __name((html) => {
            const tempDiv = document.createElement("div");
            tempDiv.innerHTML = html;
            const codeElement = tempDiv.querySelector("code");
            return codeElement?.innerHTML || html;
          }, "extractContent");
          setHighlightedOldCode(extractContent(oldHighlighted));
          setHighlightedNewCode(extractContent(newHighlighted));
          const diffChanges = generateDiff(oldCode, newCode);
          setChanges(diffChanges);
        } catch (error) {
          console.error("Failed to highlight code:", error);
          setHighlightedOldCode(oldCode);
          setHighlightedNewCode(newCode);
          setChanges(generateDiff(oldCode, newCode));
        } finally {
          setIsLoading(false);
        }
      }, "highlightCode");
      highlightCode();
    }, [oldCode, newCode, language, theme]);
    const copyToClipboard = React.useCallback(async () => {
      try {
        const diffText = view === "unified" ? changes.map((change) => {
          const prefix = change.type === "add" ? "+" : change.type === "remove" ? "-" : " ";
          return `${prefix}${change.content}`;
        }).join("\n") : `--- ${oldFilename || "old"}
+++ ${newFilename || "new"}
${oldCode}

${newCode}`;
        await navigator.clipboard.writeText(diffText);
        setCopied(true);
        setTimeout(() => setCopied(false), 2e3);
      } catch (error) {
        console.error("Failed to copy diff:", error);
      }
    }, [changes, view, oldCode, newCode, oldFilename, newFilename]);
    const filteredChanges = React.useMemo(() => {
      if (!collapseUnchanged) return changes;
      const result = [];
      let i = 0;
      while (i < changes.length) {
        const change = changes[i];
        if (change.type !== "unchanged") {
          result.push(change);
          i++;
          continue;
        }
        const unchangedStart = i;
        while (i < changes.length && changes[i].type === "unchanged") {
          i++;
        }
        const unchangedCount = i - unchangedStart;
        if (unchangedCount <= contextLines * 2) {
          for (let j = unchangedStart; j < i; j++) {
            result.push(changes[j]);
          }
        } else {
          for (let j = unchangedStart; j < unchangedStart + contextLines; j++) {
            result.push(changes[j]);
          }
          result.push({
            type: "unchanged",
            lineNumber: -1,
            content: `... ${unchangedCount - contextLines * 2} unchanged lines ...`
          });
          for (let j = i - contextLines; j < i; j++) {
            result.push(changes[j]);
          }
        }
      }
      return result;
    }, [changes, collapseUnchanged, contextLines]);
    const stats = React.useMemo(() => {
      const additions = changes.filter((c) => c.type === "add").length;
      const deletions = changes.filter((c) => c.type === "remove").length;
      return { additions, deletions };
    }, [changes]);
    const renderUnifiedView = /* @__PURE__ */ __name(() => {
      const oldLines = highlightedOldCode.split("\n");
      const newLines = highlightedNewCode.split("\n");
      return /* @__PURE__ */ jsx("div", { className: "font-mono", children: filteredChanges.map((change, index) => {
        if (change.lineNumber === -1) {
          return /* @__PURE__ */ jsx(
            "div",
            {
              className: "flex items-center justify-center py-2 text-muted-foreground bg-muted/30",
              children: /* @__PURE__ */ jsx("span", { className: "text-xs", children: change.content })
            },
            index
          );
        }
        let highlightedContent = change.content;
        if (change.type === "add" && change.newLineNumber) {
          highlightedContent = newLines[change.newLineNumber - 1] || change.content;
        } else if (change.type === "remove" && change.oldLineNumber) {
          highlightedContent = oldLines[change.oldLineNumber - 1] || change.content;
        } else if (change.type === "unchanged" && change.oldLineNumber) {
          highlightedContent = oldLines[change.oldLineNumber - 1] || change.content;
        }
        return /* @__PURE__ */ jsx(
          DiffLine,
          {
            change,
            showLineNumbers,
            isUnified: true,
            highlightedContent
          },
          index
        );
      }) });
    }, "renderUnifiedView");
    const renderSplitView = /* @__PURE__ */ __name(() => {
      const oldLines = highlightedOldCode.split("\n");
      const newLines = highlightedNewCode.split("\n");
      return /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-0", children: [
        /* @__PURE__ */ jsxs("div", { className: "border-r", children: [
          /* @__PURE__ */ jsx("div", { className: "border-b px-4 py-2 bg-red-500/10", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-red-600 dark:text-red-400", children: oldFilename || filename || "Old" }) }),
          /* @__PURE__ */ jsx("div", { className: "font-mono", children: oldLines.map((line, index) => /* @__PURE__ */ jsx(
            DiffLine,
            {
              change: {
                type: "remove",
                lineNumber: index + 1,
                content: line,
                oldLineNumber: index + 1
              },
              showLineNumbers,
              highlightedContent: line
            },
            `old-${index}`
          )) })
        ] }),
        /* @__PURE__ */ jsxs("div", { children: [
          /* @__PURE__ */ jsx("div", { className: "border-b px-4 py-2 bg-green-500/10", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-green-600 dark:text-green-400", children: newFilename || filename || "New" }) }),
          /* @__PURE__ */ jsx("div", { className: "font-mono", children: newLines.map((line, index) => /* @__PURE__ */ jsx(
            DiffLine,
            {
              change: {
                type: "add",
                lineNumber: index + 1,
                content: line,
                newLineNumber: index + 1
              },
              showLineNumbers,
              highlightedContent: line
            },
            `new-${index}`
          )) })
        ] })
      ] });
    }, "renderSplitView");
    return /* @__PURE__ */ jsxs(
      "div",
      {
        ref,
        className: cn(codeDiffVariants({ theme, size, className })),
        ...props,
        children: [
          /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b px-4 py-2", children: [
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
              /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
                /* @__PURE__ */ jsx(GitCompare, { className: "h-4 w-4 text-muted-foreground" }),
                filename && /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: filename }),
                language && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: language })
              ] }),
              /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
                /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
                  /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3 text-green-500" }),
                  stats.additions
                ] }),
                /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1", children: [
                  /* @__PURE__ */ jsx(Minus, { className: "h-3 w-3 text-red-500" }),
                  stats.deletions
                ] })
              ] })
            ] }),
            /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
              /* @__PURE__ */ jsxs(
                ToggleGroup,
                {
                  type: "single",
                  value: view,
                  onValueChange: (value) => value && setView(value),
                  size: "sm",
                  children: [
                    /* @__PURE__ */ jsx(ToggleGroupItem, { value: "unified", "aria-label": "Unified view", children: /* @__PURE__ */ jsx(Square, { className: "h-3 w-3" }) }),
                    /* @__PURE__ */ jsx(ToggleGroupItem, { value: "split", "aria-label": "Split view", children: /* @__PURE__ */ jsx(SplitSquareHorizontal, { className: "h-3 w-3" }) })
                  ]
                }
              ),
              showCopyButton && /* @__PURE__ */ jsxs(
                Button,
                {
                  variant: "ghost",
                  size: "sm",
                  onClick: copyToClipboard,
                  className: "h-8 w-8 p-0",
                  children: [
                    copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-green-500" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }),
                    /* @__PURE__ */ jsx("span", { className: "sr-only", children: copied ? "Copied" : "Copy diff" })
                  ]
                }
              )
            ] })
          ] }),
          /* @__PURE__ */ jsx("div", { className: "overflow-auto", style: { maxHeight }, children: isLoading ? /* @__PURE__ */ jsx("div", { className: "p-4 space-y-2", children: Array.from({ length: 10 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
            showLineNumbers && /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-8" }),
            /* @__PURE__ */ jsx(Skeleton, { className: "h-4 flex-1" })
          ] }, i)) }) : view === "unified" ? renderUnifiedView() : renderSplitView() })
        ]
      }
    );
  }
);
CodeDiff.displayName = "CodeDiff";

export { CodeDiff, codeDiffVariants };
