import { relative } from "node:path";
import * as Diff from "diff";
import { Box } from "ink";
import { useTerminalWidth } from "../hooks/useTerminalWidth";
import { colors } from "./colors";
import { Text } from "./Text";

// Helper to format path as relative with ../
/**
 * Formats a file path for display (matches Claude Code style):
 * - Files within cwd: relative path without ./ prefix
 * - Files outside cwd: full absolute path
 */
function formatDisplayPath(filePath: string): string {
  const cwd = process.cwd();
  const relativePath = relative(cwd, filePath);
  // If path goes outside cwd (starts with ..), show full absolute path
  if (relativePath.startsWith("..")) {
    return filePath;
  }
  return relativePath;
}

// Helper to count lines in a string
function countLines(str: string): number {
  if (!str) return 0;
  return str.split("\n").length;
}

// Helper to render a diff line with word-level highlighting
interface DiffLineProps {
  lineNumber: number;
  type: "add" | "remove";
  content: string;
  compareContent?: string; // The other version to compare against for word diff
  columns: number;
  showLineNumbers?: boolean; // Whether to show line numbers (default true)
}

function DiffLine({
  lineNumber,
  type,
  content,
  compareContent,
  columns,
  showLineNumbers = true,
}: DiffLineProps) {
  const prefix = type === "add" ? "+" : "-";
  const lineBg =
    type === "add" ? colors.diff.addedLineBg : colors.diff.removedLineBg;
  const wordBg =
    type === "add" ? colors.diff.addedWordBg : colors.diff.removedWordBg;

  const gutterWidth = 4; // "    " indent to align with tool return prefix
  const contentWidth = Math.max(0, columns - gutterWidth);

  // Build the line prefix (with or without line number)
  const linePrefix = showLineNumbers
    ? `${lineNumber} ${prefix}  `
    : `${prefix} `;

  // If we have something to compare against, do word-level diff
  if (compareContent !== undefined && content.trim() && compareContent.trim()) {
    const wordDiffs =
      type === "add"
        ? Diff.diffWords(compareContent, content)
        : Diff.diffWords(content, compareContent);

    return (
      <Box flexDirection="row">
        <Box width={gutterWidth} flexShrink={0}>
          <Text>{"    "}</Text>
        </Box>
        <Box flexGrow={1} width={contentWidth}>
          <Text wrap="wrap">
            <Text backgroundColor={lineBg} color={colors.diff.textOnDark}>
              {linePrefix}
            </Text>
            {wordDiffs.map((part, i) => {
              if (part.added && type === "add") {
                // This part was added (show with brighter background, black text)
                return (
                  <Text
                    key={`word-${i}-${part.value.substring(0, 10)}`}
                    backgroundColor={wordBg}
                    color={colors.diff.textOnHighlight}
                  >
                    {part.value}
                  </Text>
                );
              } else if (part.removed && type === "remove") {
                // This part was removed (show with brighter background, black text)
                return (
                  <Text
                    key={`word-${i}-${part.value.substring(0, 10)}`}
                    backgroundColor={wordBg}
                    color={colors.diff.textOnHighlight}
                  >
                    {part.value}
                  </Text>
                );
              } else if (!part.added && !part.removed) {
                // Unchanged part (show with line background, white text)
                return (
                  <Text
                    key={`word-${i}-${part.value.substring(0, 10)}`}
                    backgroundColor={lineBg}
                    color={colors.diff.textOnDark}
                  >
                    {part.value}
                  </Text>
                );
              }
              // Skip parts that don't belong in this line
              return null;
            })}
          </Text>
        </Box>
      </Box>
    );
  }

  // No comparison, just show the whole line with one background
  return (
    <Box flexDirection="row">
      <Box width={gutterWidth} flexShrink={0}>
        <Text>{"    "}</Text>
      </Box>
      <Box flexGrow={1} width={contentWidth}>
        <Text
          backgroundColor={lineBg}
          color={colors.diff.textOnDark}
          wrap="wrap"
        >
          {`${linePrefix}${content}`}
        </Text>
      </Box>
    </Box>
  );
}

interface WriteRendererProps {
  filePath: string;
  content: string;
}

export function WriteRenderer({ filePath, content }: WriteRendererProps) {
  const columns = useTerminalWidth();
  const relativePath = formatDisplayPath(filePath);
  const lines = content.split("\n");
  const lineCount = lines.length;

  const gutterWidth = 4; // "    " indent to align with tool return prefix
  const contentWidth = Math.max(0, columns - gutterWidth);

  return (
    <Box flexDirection="column">
      <Box flexDirection="row">
        <Box width={gutterWidth} flexShrink={0}>
          <Text>
            {"  "}
            <Text dimColor>⎿</Text>
          </Text>
        </Box>
        <Box flexGrow={1} width={contentWidth}>
          <Text wrap="wrap">
            Wrote <Text bold>{lineCount}</Text> line
            {lineCount !== 1 ? "s" : ""} to <Text bold>{relativePath}</Text>
          </Text>
        </Box>
      </Box>
      {lines.map((line, i) => (
        <Box key={`line-${i}-${line.substring(0, 20)}`} flexDirection="row">
          <Box width={gutterWidth} flexShrink={0}>
            <Text>{"    "}</Text>
          </Box>
          <Box flexGrow={1} width={contentWidth}>
            <Text wrap="wrap">{line}</Text>
          </Box>
        </Box>
      ))}
    </Box>
  );
}

interface EditRendererProps {
  filePath: string;
  oldString: string;
  newString: string;
  showLineNumbers?: boolean; // Whether to show line numbers (default true)
}

export function EditRenderer({
  filePath,
  oldString,
  newString,
  showLineNumbers = true,
}: EditRendererProps) {
  const columns = useTerminalWidth();
  const relativePath = formatDisplayPath(filePath);
  const oldLines = oldString.split("\n");
  const newLines = newString.split("\n");

  // For the summary
  const additions = newLines.length;
  const removals = oldLines.length;

  // Try to match up lines for word-level diff
  // This is a simple approach - for single-line changes, compare directly
  // For multi-line, we could do more sophisticated matching
  const singleLineEdit = oldLines.length === 1 && newLines.length === 1;

  const gutterWidth = 4; // "    " indent to align with tool return prefix
  const contentWidth = Math.max(0, columns - gutterWidth);

  return (
    <Box flexDirection="column">
      <Box flexDirection="row">
        <Box width={gutterWidth} flexShrink={0}>
          <Text>
            {"  "}
            <Text dimColor>⎿</Text>
          </Text>
        </Box>
        <Box flexGrow={1} width={contentWidth}>
          <Text wrap="wrap">
            Updated <Text bold>{relativePath}</Text> with{" "}
            <Text bold>{additions}</Text> addition
            {additions !== 1 ? "s" : ""} and <Text bold>{removals}</Text>{" "}
            removal
            {removals !== 1 ? "s" : ""}
          </Text>
        </Box>
      </Box>

      {/* Show removals */}
      {oldLines.map((line, i) => (
        <DiffLine
          key={`old-${i}-${line.substring(0, 20)}`}
          lineNumber={i + 1}
          type="remove"
          content={line}
          compareContent={singleLineEdit ? newLines[0] : undefined}
          columns={columns}
          showLineNumbers={showLineNumbers}
        />
      ))}

      {/* Show additions */}
      {newLines.map((line, i) => (
        <DiffLine
          key={`new-${i}-${line.substring(0, 20)}`}
          lineNumber={i + 1}
          type="add"
          content={line}
          compareContent={singleLineEdit ? oldLines[0] : undefined}
          columns={columns}
          showLineNumbers={showLineNumbers}
        />
      ))}
    </Box>
  );
}

interface MultiEditRendererProps {
  filePath: string;
  edits: Array<{
    old_string: string;
    new_string: string;
  }>;
  showLineNumbers?: boolean; // Whether to show line numbers (default true)
}

export function MultiEditRenderer({
  filePath,
  edits,
  showLineNumbers = true,
}: MultiEditRendererProps) {
  const columns = useTerminalWidth();
  const relativePath = formatDisplayPath(filePath);

  // Count total additions and removals
  let totalAdditions = 0;
  let totalRemovals = 0;

  edits.forEach((edit) => {
    totalAdditions += countLines(edit.new_string);
    totalRemovals += countLines(edit.old_string);
  });

  const gutterWidth = 4; // "    " indent to align with tool return prefix
  const contentWidth = Math.max(0, columns - gutterWidth);

  return (
    <Box flexDirection="column">
      <Box flexDirection="row">
        <Box width={gutterWidth} flexShrink={0}>
          <Text>
            {"  "}
            <Text dimColor>⎿</Text>
          </Text>
        </Box>
        <Box flexGrow={1} width={contentWidth}>
          <Text wrap="wrap">
            Updated <Text bold>{relativePath}</Text> with{" "}
            <Text bold>{totalAdditions}</Text> addition
            {totalAdditions !== 1 ? "s" : ""} and{" "}
            <Text bold>{totalRemovals}</Text> removal
            {totalRemovals !== 1 ? "s" : ""}
          </Text>
        </Box>
      </Box>

      {/* For multi-edit, show each edit sequentially */}
      {edits.map((edit, index) => {
        const oldLines = edit.old_string.split("\n");
        const newLines = edit.new_string.split("\n");
        const singleLineEdit = oldLines.length === 1 && newLines.length === 1;

        return (
          <Box
            key={`edit-${index}-${edit.old_string.substring(0, 20)}-${edit.new_string.substring(0, 20)}`}
            flexDirection="column"
          >
            {oldLines.map((line, i) => (
              <DiffLine
                key={`old-${index}-${i}-${line.substring(0, 20)}`}
                lineNumber={i + 1}
                type="remove"
                content={line}
                compareContent={
                  singleLineEdit && i === 0 ? newLines[0] : undefined
                }
                columns={columns}
                showLineNumbers={showLineNumbers}
              />
            ))}
            {newLines.map((line, i) => (
              <DiffLine
                key={`new-${index}-${i}-${line.substring(0, 20)}`}
                lineNumber={i + 1}
                type="add"
                content={line}
                compareContent={
                  singleLineEdit && i === 0 ? oldLines[0] : undefined
                }
                columns={columns}
                showLineNumbers={showLineNumbers}
              />
            ))}
          </Box>
        );
      })}
    </Box>
  );
}
