"use client";

import { useState, useEffect, useMemo, useCallback } from "react";
import { getElectronAPI } from "@/lib/electron";
import { cn } from "@/lib/utils";
import {
  File,
  FileText,
  FilePlus,
  FileX,
  FilePen,
  ChevronDown,
  ChevronRight,
  Loader2,
  RefreshCw,
  GitBranch,
  AlertCircle,
} from "lucide-react";
import { Button } from "./button";
import type { FileStatus } from "@/types/electron";

interface GitDiffPanelProps {
  projectPath: string;
  featureId: string;
  className?: string;
  /** Whether to show the panel in a compact/minimized state initially */
  compact?: boolean;
  /** Whether worktrees are enabled - if false, shows diffs from main project */
  useWorktrees?: boolean;
}

interface ParsedDiffHunk {
  header: string;
  lines: {
    type: "context" | "addition" | "deletion" | "header";
    content: string;
    lineNumber?: { old?: number; new?: number };
  }[];
}

interface ParsedFileDiff {
  filePath: string;
  hunks: ParsedDiffHunk[];
  isNew?: boolean;
  isDeleted?: boolean;
  isRenamed?: boolean;
}

const getFileIcon = (status: string) => {
  switch (status) {
    case "A":
    case "?":
      return <FilePlus className="w-4 h-4 text-green-500" />;
    case "D":
      return <FileX className="w-4 h-4 text-red-500" />;
    case "M":
    case "U":
      return <FilePen className="w-4 h-4 text-amber-500" />;
    case "R":
    case "C":
      return <File className="w-4 h-4 text-blue-500" />;
    default:
      return <FileText className="w-4 h-4 text-muted-foreground" />;
  }
};

const getStatusBadgeColor = (status: string) => {
  switch (status) {
    case "A":
    case "?":
      return "bg-green-500/20 text-green-400 border-green-500/30";
    case "D":
      return "bg-red-500/20 text-red-400 border-red-500/30";
    case "M":
    case "U":
      return "bg-amber-500/20 text-amber-400 border-amber-500/30";
    case "R":
    case "C":
      return "bg-blue-500/20 text-blue-400 border-blue-500/30";
    default:
      return "bg-muted text-muted-foreground border-border";
  }
};

const getStatusDisplayName = (status: string) => {
  switch (status) {
    case "A":
      return "Added";
    case "?":
      return "Untracked";
    case "D":
      return "Deleted";
    case "M":
      return "Modified";
    case "U":
      return "Updated";
    case "R":
      return "Renamed";
    case "C":
      return "Copied";
    default:
      return "Changed";
  }
};

/**
 * Parse unified diff format into structured data
 */
function parseDiff(diffText: string): ParsedFileDiff[] {
  if (!diffText) return [];

  const files: ParsedFileDiff[] = [];
  const lines = diffText.split("\n");
  let currentFile: ParsedFileDiff | null = null;
  let currentHunk: ParsedDiffHunk | null = null;
  let oldLineNum = 0;
  let newLineNum = 0;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];

    // New file diff
    if (line.startsWith("diff --git")) {
      if (currentFile) {
        if (currentHunk) {
          currentFile.hunks.push(currentHunk);
        }
        files.push(currentFile);
      }
      // Extract file path from diff header
      const match = line.match(/diff --git a\/(.*?) b\/(.*)/);
      currentFile = {
        filePath: match ? match[2] : "unknown",
        hunks: [],
      };
      currentHunk = null;
      continue;
    }

    // New file indicator
    if (line.startsWith("new file mode")) {
      if (currentFile) currentFile.isNew = true;
      continue;
    }

    // Deleted file indicator
    if (line.startsWith("deleted file mode")) {
      if (currentFile) currentFile.isDeleted = true;
      continue;
    }

    // Renamed file indicator
    if (line.startsWith("rename from") || line.startsWith("rename to")) {
      if (currentFile) currentFile.isRenamed = true;
      continue;
    }

    // Skip index, ---/+++ lines
    if (
      line.startsWith("index ") ||
      line.startsWith("--- ") ||
      line.startsWith("+++ ")
    ) {
      continue;
    }

    // Hunk header
    if (line.startsWith("@@")) {
      if (currentHunk && currentFile) {
        currentFile.hunks.push(currentHunk);
      }
      // Parse line numbers from @@ -old,count +new,count @@
      const hunkMatch = line.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
      oldLineNum = hunkMatch ? parseInt(hunkMatch[1], 10) : 1;
      newLineNum = hunkMatch ? parseInt(hunkMatch[2], 10) : 1;
      currentHunk = {
        header: line,
        lines: [{ type: "header", content: line }],
      };
      continue;
    }

    // Diff content lines
    if (currentHunk) {
      if (line.startsWith("+")) {
        currentHunk.lines.push({
          type: "addition",
          content: line.substring(1),
          lineNumber: { new: newLineNum },
        });
        newLineNum++;
      } else if (line.startsWith("-")) {
        currentHunk.lines.push({
          type: "deletion",
          content: line.substring(1),
          lineNumber: { old: oldLineNum },
        });
        oldLineNum++;
      } else if (line.startsWith(" ") || line === "") {
        currentHunk.lines.push({
          type: "context",
          content: line.substring(1) || "",
          lineNumber: { old: oldLineNum, new: newLineNum },
        });
        oldLineNum++;
        newLineNum++;
      }
    }
  }

  // Don't forget the last file and hunk
  if (currentFile) {
    if (currentHunk) {
      currentFile.hunks.push(currentHunk);
    }
    files.push(currentFile);
  }

  return files;
}

function DiffLine({
  type,
  content,
  lineNumber,
}: {
  type: "context" | "addition" | "deletion" | "header";
  content: string;
  lineNumber?: { old?: number; new?: number };
}) {
  const bgClass = {
    context: "bg-transparent",
    addition: "bg-green-500/10",
    deletion: "bg-red-500/10",
    header: "bg-blue-500/10",
  };

  const textClass = {
    context: "text-foreground-secondary",
    addition: "text-green-400",
    deletion: "text-red-400",
    header: "text-blue-400",
  };

  const prefix = {
    context: " ",
    addition: "+",
    deletion: "-",
    header: "",
  };

  if (type === "header") {
    return (
      <div className={cn("px-2 py-1 font-mono text-xs", bgClass[type], textClass[type])}>
        {content}
      </div>
    );
  }

  return (
    <div className={cn("flex font-mono text-xs", bgClass[type])}>
      <span className="w-12 flex-shrink-0 text-right pr-2 text-muted-foreground select-none border-r border-border-glass">
        {lineNumber?.old ?? ""}
      </span>
      <span className="w-12 flex-shrink-0 text-right pr-2 text-muted-foreground select-none border-r border-border-glass">
        {lineNumber?.new ?? ""}
      </span>
      <span className={cn("w-4 flex-shrink-0 text-center select-none", textClass[type])}>
        {prefix[type]}
      </span>
      <span className={cn("flex-1 px-2 whitespace-pre-wrap break-all", textClass[type])}>
        {content || "\u00A0"}
      </span>
    </div>
  );
}

function FileDiffSection({
  fileDiff,
  isExpanded,
  onToggle,
}: {
  fileDiff: ParsedFileDiff;
  isExpanded: boolean;
  onToggle: () => void;
}) {
  const additions = fileDiff.hunks.reduce(
    (acc, hunk) => acc + hunk.lines.filter((l) => l.type === "addition").length,
    0
  );
  const deletions = fileDiff.hunks.reduce(
    (acc, hunk) => acc + hunk.lines.filter((l) => l.type === "deletion").length,
    0
  );

  return (
    <div className="border border-border rounded-lg overflow-hidden">
      <button
        onClick={onToggle}
        className="w-full px-3 py-2 flex items-center gap-2 text-left bg-card hover:bg-accent/50 transition-colors"
      >
        {isExpanded ? (
          <ChevronDown className="w-4 h-4 text-muted-foreground flex-shrink-0" />
        ) : (
          <ChevronRight className="w-4 h-4 text-muted-foreground flex-shrink-0" />
        )}
        <FileText className="w-4 h-4 text-muted-foreground flex-shrink-0" />
        <span className="flex-1 text-sm font-mono truncate text-foreground">
          {fileDiff.filePath}
        </span>
        <div className="flex items-center gap-2 flex-shrink-0">
          {fileDiff.isNew && (
            <span className="text-xs px-1.5 py-0.5 rounded bg-green-500/20 text-green-400">
              new
            </span>
          )}
          {fileDiff.isDeleted && (
            <span className="text-xs px-1.5 py-0.5 rounded bg-red-500/20 text-red-400">
              deleted
            </span>
          )}
          {fileDiff.isRenamed && (
            <span className="text-xs px-1.5 py-0.5 rounded bg-blue-500/20 text-blue-400">
              renamed
            </span>
          )}
          {additions > 0 && (
            <span className="text-xs text-green-400">+{additions}</span>
          )}
          {deletions > 0 && (
            <span className="text-xs text-red-400">-{deletions}</span>
          )}
        </div>
      </button>
      {isExpanded && (
        <div className="bg-background border-t border-border max-h-[400px] overflow-y-auto scrollbar-visible">
          {fileDiff.hunks.map((hunk, hunkIndex) => (
            <div key={hunkIndex} className="border-b border-border-glass last:border-b-0">
              {hunk.lines.map((line, lineIndex) => (
                <DiffLine
                  key={lineIndex}
                  type={line.type}
                  content={line.content}
                  lineNumber={line.lineNumber}
                />
              ))}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

export function GitDiffPanel({
  projectPath,
  featureId,
  className,
  compact = true,
  useWorktrees = false,
}: GitDiffPanelProps) {
  const [isExpanded, setIsExpanded] = useState(!compact);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [files, setFiles] = useState<FileStatus[]>([]);
  const [diffContent, setDiffContent] = useState<string>("");
  const [expandedFiles, setExpandedFiles] = useState<Set<string>>(new Set());

  const loadDiffs = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const api = getElectronAPI();

      // Use worktree API if worktrees are enabled, otherwise use git API for main project
      if (useWorktrees) {
        if (!api?.worktree?.getDiffs) {
          throw new Error("Worktree API not available");
        }
        const result = await api.worktree.getDiffs(projectPath, featureId);
        if (result.success) {
          setFiles(result.files || []);
          setDiffContent(result.diff || "");
        } else {
          setError(result.error || "Failed to load diffs");
        }
      } else {
        // Use git API for main project diffs
        if (!api?.git?.getDiffs) {
          throw new Error("Git API not available");
        }
        const result = await api.git.getDiffs(projectPath);
        if (result.success) {
          setFiles(result.files || []);
          setDiffContent(result.diff || "");
        } else {
          setError(result.error || "Failed to load diffs");
        }
      }
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to load diffs");
    } finally {
      setIsLoading(false);
    }
  }, [projectPath, featureId, useWorktrees]);

  // Load diffs when expanded
  useEffect(() => {
    if (isExpanded) {
      loadDiffs();
    }
  }, [isExpanded, loadDiffs]);

  const parsedDiffs = useMemo(() => parseDiff(diffContent), [diffContent]);

  const toggleFile = (filePath: string) => {
    setExpandedFiles((prev) => {
      const next = new Set(prev);
      if (next.has(filePath)) {
        next.delete(filePath);
      } else {
        next.add(filePath);
      }
      return next;
    });
  };

  const expandAllFiles = () => {
    setExpandedFiles(new Set(parsedDiffs.map((d) => d.filePath)));
  };

  const collapseAllFiles = () => {
    setExpandedFiles(new Set());
  };

  // Total stats
  const totalAdditions = parsedDiffs.reduce(
    (acc, file) =>
      acc +
      file.hunks.reduce(
        (hAcc, hunk) =>
          hAcc + hunk.lines.filter((l) => l.type === "addition").length,
        0
      ),
    0
  );
  const totalDeletions = parsedDiffs.reduce(
    (acc, file) =>
      acc +
      file.hunks.reduce(
        (hAcc, hunk) =>
          hAcc + hunk.lines.filter((l) => l.type === "deletion").length,
        0
      ),
    0
  );

  return (
    <div
      className={cn(
        "rounded-xl border border-border bg-card backdrop-blur-sm overflow-hidden",
        className
      )}
      data-testid="git-diff-panel"
    >
      {/* Header */}
      <button
        onClick={() => setIsExpanded(!isExpanded)}
        className="w-full px-4 py-3 flex items-center justify-between bg-card hover:bg-accent/50 transition-colors text-left flex-shrink-0"
        data-testid="git-diff-panel-toggle"
      >
        <div className="flex items-center gap-2">
          {isExpanded ? (
            <ChevronDown className="w-4 h-4 text-muted-foreground" />
          ) : (
            <ChevronRight className="w-4 h-4 text-muted-foreground" />
          )}
          <GitBranch className="w-4 h-4 text-brand-500" />
          <span className="font-medium text-sm text-foreground">Git Changes</span>
        </div>
        <div className="flex items-center gap-3 text-xs">
          {!isExpanded && files.length > 0 && (
            <>
              <span className="text-muted-foreground">
                {files.length} {files.length === 1 ? "file" : "files"}
              </span>
              {totalAdditions > 0 && (
                <span className="text-green-400">+{totalAdditions}</span>
              )}
              {totalDeletions > 0 && (
                <span className="text-red-400">-{totalDeletions}</span>
              )}
            </>
          )}
        </div>
      </button>

      {/* Content */}
      {isExpanded && (
        <div className="border-t border-border">
          {isLoading ? (
            <div className="flex items-center justify-center gap-2 py-8 text-muted-foreground">
              <Loader2 className="w-5 h-5 animate-spin" />
              <span className="text-sm">Loading changes...</span>
            </div>
          ) : error ? (
            <div className="flex flex-col items-center justify-center gap-2 py-8 text-muted-foreground">
              <AlertCircle className="w-5 h-5 text-amber-500" />
              <span className="text-sm">{error}</span>
              <Button
                variant="ghost"
                size="sm"
                onClick={loadDiffs}
                className="mt-2"
              >
                <RefreshCw className="w-4 h-4 mr-2" />
                Retry
              </Button>
            </div>
          ) : files.length === 0 ? (
            <div className="flex items-center justify-center gap-2 py-8 text-muted-foreground">
              <span className="text-sm">No changes detected</span>
            </div>
          ) : (
            <div>
              {/* Summary bar */}
              <div className="p-4 pb-2 border-b border-border-glass">
                <div className="flex items-center justify-between">
                  <div className="flex items-center gap-4 flex-wrap">
                    {(() => {
                      // Group files by status
                      const statusGroups = files.reduce((acc, file) => {
                        const status = file.status;
                        if (!acc[status]) {
                          acc[status] = {
                            count: 0,
                            statusText: getStatusDisplayName(status),
                            files: []
                          };
                        }
                        acc[status].count += 1;
                        acc[status].files.push(file.path);
                        return acc;
                      }, {} as Record<string, {count: number, statusText: string, files: string[]}>);

                      return Object.entries(statusGroups).map(([status, group]) => (
                        <div
                          key={status}
                          className="flex items-center gap-1.5"
                          title={group.files.join('\n')}
                          data-testid={`git-status-group-${status.toLowerCase()}`}
                        >
                          {getFileIcon(status)}
                          <span
                            className={cn(
                              "text-xs px-1.5 py-0.5 rounded border font-medium",
                              getStatusBadgeColor(status)
                            )}
                          >
                            {group.count} {group.statusText}
                          </span>
                        </div>
                      ));
                    })()}
                  </div>
                  <div className="flex items-center gap-2">
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={expandAllFiles}
                      className="text-xs h-7"
                    >
                      Expand All
                    </Button>
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={collapseAllFiles}
                      className="text-xs h-7"
                    >
                      Collapse All
                    </Button>
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={loadDiffs}
                      className="text-xs h-7"
                    >
                      <RefreshCw className="w-3 h-3 mr-1" />
                      Refresh
                    </Button>
                  </div>
                </div>

                {/* Stats */}
                <div className="flex items-center gap-4 text-sm mt-2">
                  <span className="text-muted-foreground">
                    {files.length} {files.length === 1 ? "file" : "files"} changed
                  </span>
                  {totalAdditions > 0 && (
                    <span className="text-green-400">
                      +{totalAdditions} additions
                    </span>
                  )}
                  {totalDeletions > 0 && (
                    <span className="text-red-400">
                      -{totalDeletions} deletions
                    </span>
                  )}
                </div>
              </div>

              {/* File diffs */}
              <div className="p-4 space-y-3">
                {parsedDiffs.map((fileDiff) => (
                  <FileDiffSection
                    key={fileDiff.filePath}
                    fileDiff={fileDiff}
                    isExpanded={expandedFiles.has(fileDiff.filePath)}
                    onToggle={() => toggleFile(fileDiff.filePath)}
                  />
                ))}
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
}
