import { getTextInput, type ToolInput } from "../../core/io/input-types";
import type { ToolResult } from "../../core/io/output-types";
import type { ToolContext } from "../../core/plugins/plugin-types";
import type { TextDiffOptions } from "./index";

function lcs(a: string[], b: string[]): number[][] {
  const m = a.length;
  const n = b.length;
  const dp: number[][] = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));

  for (let i = 1; i <= m; i++) {
    for (let j = 1; j <= n; j++) {
      if (a[i - 1] === b[j - 1]) {
        dp[i][j] = dp[i - 1][j - 1] + 1;
      } else {
        dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
      }
    }
  }

  return dp;
}

function computeDiff(a: string[], b: string[], dp: number[][]): Array<{ type: "same" | "added" | "removed"; line: string }> {
  const result: Array<{ type: "same" | "added" | "removed"; line: string }> = [];
  let i = a.length;
  let j = b.length;

  while (i > 0 || j > 0) {
    if (i > 0 && j > 0 && a[i - 1] === b[j - 1]) {
      result.unshift({ type: "same", line: a[i - 1] });
      i--;
      j--;
    } else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
      result.unshift({ type: "added", line: b[j - 1] });
      j--;
    } else if (i > 0) {
      result.unshift({ type: "removed", line: a[i - 1] });
      i--;
    }
  }

  return result;
}

export async function runTextDiff(
  input: ToolInput,
  options: TextDiffOptions,
  _context: ToolContext
): Promise<ToolResult> {
  void _context;
  let textA = getTextInput(input, "original");
  let textB = getTextInput(input, "modified");

  if ((!textA || !textB) && input.text) {
    const separatorIndex = input.text.indexOf("\n---\n");
    if (separatorIndex >= 0) {
      textA = input.text.substring(0, separatorIndex);
      textB = input.text.substring(separatorIndex + 5);
    }
  }

  if (!textA.trim() && !textB.trim()) {
    throw new Error("No text provided. Fill both original and modified text.");
  }

  if (!textA.trim() || !textB.trim()) {
    throw new Error("Both original and modified text are required.");
  }

  if (options.ignoreWhitespace) {
    textA = textA.replace(/\s+/g, " ").trim();
    textB = textB.replace(/\s+/g, " ").trim();
  }

  const linesA = textA.split("\n");
  const linesB = textB.split("\n");

  const dp = lcs(linesA, linesB);
  const diff = computeDiff(linesA, linesB, dp);

  const output = diff.map((entry) => {
    switch (entry.type) {
      case "added":
        return `+ ${entry.line}`;
      case "removed":
        return `- ${entry.line}`;
      case "same":
        return `  ${entry.line}`;
    }
  }).join("\n");

  const added = diff.filter((d) => d.type === "added").length;
  const removed = diff.filter((d) => d.type === "removed").length;
  const same = diff.filter((d) => d.type === "same").length;

  const summary = `\n\n--- Summary: ${added} added, ${removed} removed, ${same} unchanged ---`;

  return {
    type: "text",
    value: output + summary,
  };
}
