import type React from "react";
import { memo, useEffect, useMemo, useRef, useState } from "react";
import type { GitDiffLine } from "@/features/git/types/git-types";
import { Button } from "@/ui/button";

interface InlineDiffProps {
  lineNumber: number;
  type: "added" | "modified" | "deleted";
  diffLines: GitDiffLine[];
  fontSize: number;
  fontFamily: string;
  lineHeight: number;
  onClose: () => void;
  onRevert?: (lineNumber: number, originalContent: string) => void;
}

function highlightCharDiff(
  oldStr: string,
  newStr: string,
): { oldHighlights: boolean[]; newHighlights: boolean[] } {
  const oldHighlights: boolean[] = Array.from({ length: oldStr.length }, () => false);
  const newHighlights: boolean[] = Array.from({ length: newStr.length }, () => false);

  let prefixLen = 0;
  while (
    prefixLen < oldStr.length &&
    prefixLen < newStr.length &&
    oldStr[prefixLen] === newStr[prefixLen]
  ) {
    prefixLen++;
  }

  let suffixLen = 0;
  while (
    suffixLen < oldStr.length - prefixLen &&
    suffixLen < newStr.length - prefixLen &&
    oldStr[oldStr.length - 1 - suffixLen] === newStr[newStr.length - 1 - suffixLen]
  ) {
    suffixLen++;
  }

  for (let i = prefixLen; i < oldStr.length - suffixLen; i++) {
    oldHighlights[i] = true;
  }
  for (let i = prefixLen; i < newStr.length - suffixLen; i++) {
    newHighlights[i] = true;
  }

  return { oldHighlights, newHighlights };
}

function InlineDiffComponent({
  lineNumber,
  type,
  diffLines,
  fontSize,
  fontFamily,
  lineHeight,
  onClose,
  onRevert,
}: InlineDiffProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [isHovered, setIsHovered] = useState(false);

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === "Escape") {
        onClose();
      }
    };

    const handleClickOutside = (e: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        onClose();
      }
    };

    document.addEventListener("keydown", handleKeyDown);
    const timer = setTimeout(() => {
      document.addEventListener("mousedown", handleClickOutside);
    }, 100);

    return () => {
      document.removeEventListener("keydown", handleKeyDown);
      document.removeEventListener("mousedown", handleClickOutside);
      clearTimeout(timer);
    };
  }, [onClose]);

  const linesToShow = diffLines.filter((line) => {
    if (type === "added") {
      return line.new_line_number === lineNumber + 1 && line.line_type === "added";
    }
    if (type === "deleted") {
      return line.old_line_number === lineNumber + 1 && line.line_type === "removed";
    }
    return (
      (line.old_line_number === lineNumber + 1 && line.line_type === "removed") ||
      (line.new_line_number === lineNumber + 1 && line.line_type === "added")
    );
  });

  const charHighlights = useMemo(() => {
    if (type !== "modified") return null;

    const removedLine = linesToShow.find((l) => l.line_type === "removed");
    const addedLine = linesToShow.find((l) => l.line_type === "added");

    if (removedLine && addedLine) {
      return highlightCharDiff(removedLine.content, addedLine.content);
    }
    return null;
  }, [type, linesToShow]);

  const renderHighlightedContent = (
    content: string,
    highlights: boolean[] | null,
    lineType: string,
  ) => {
    if (!highlights) {
      return <span style={{ whiteSpace: "pre", overflow: "hidden", flex: 1 }}>{content}</span>;
    }

    const segments: React.ReactElement[] = [];
    let currentSegment = "";
    let currentHighlighted = false;

    for (let i = 0; i <= content.length; i++) {
      const isHighlighted = i < content.length ? highlights[i] : false;

      if (i === content.length || isHighlighted !== currentHighlighted) {
        if (currentSegment) {
          segments.push(
            <span
              key={segments.length}
              style={{
                whiteSpace: "pre",
                backgroundColor: currentHighlighted
                  ? lineType === "removed"
                    ? "rgba(248, 81, 73, 0.4)"
                    : "rgba(46, 160, 67, 0.4)"
                  : "transparent",
                borderRadius: currentHighlighted ? "2px" : "0",
              }}
            >
              {currentSegment}
            </span>,
          );
        }
        currentSegment = i < content.length ? content[i] : "";
        currentHighlighted = isHighlighted;
      } else {
        currentSegment += content[i];
      }
    }

    return <span style={{ whiteSpace: "pre", overflow: "hidden", flex: 1 }}>{segments}</span>;
  };

  const getLineBackground = (lineType: GitDiffLine["line_type"]) => {
    switch (lineType) {
      case "added":
        return "rgba(46, 160, 67, 0.2)";
      case "removed":
        return "rgba(248, 81, 73, 0.2)";
      default:
        return "transparent";
    }
  };

  const topPosition = (lineNumber + 1) * lineHeight;

  const handleRevert = () => {
    if (!onRevert) return;
    const removedLine = linesToShow.find((l) => l.line_type === "removed");
    if (removedLine) {
      onRevert(lineNumber, removedLine.content);
    }
    onClose();
  };

  return (
    <div
      ref={containerRef}
      style={{
        position: "absolute",
        top: `${topPosition}px`,
        left: 0,
        right: 0,
        pointerEvents: "auto",
      }}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      {linesToShow.length > 0 ? (
        linesToShow.map((line, idx) => (
          <div
            key={idx}
            style={{
              position: "relative",
              display: "flex",
              height: `${lineHeight}px`,
              lineHeight: `${lineHeight}px`,
              fontSize: `${fontSize}px`,
              fontFamily,
              backgroundColor: getLineBackground(line.line_type),
              paddingLeft: "1rem",
            }}
          >
            <span
              style={{
                color: "var(--text, #d4d4d4)",
                display: "flex",
                flex: 1,
                overflow: "hidden",
              }}
            >
              {renderHighlightedContent(
                line.content,
                charHighlights
                  ? line.line_type === "removed"
                    ? charHighlights.oldHighlights
                    : charHighlights.newHighlights
                  : null,
                line.line_type,
              )}
            </span>

            {isHovered && idx === 0 && (
              <div
                style={{
                  position: "absolute",
                  right: "8px",
                  top: "50%",
                  transform: "translateY(-50%)",
                  display: "flex",
                  gap: "4px",
                  backgroundColor: "var(--secondary-bg, #2a2a2a)",
                  borderRadius: "4px",
                  padding: "2px 4px",
                }}
              >
                {onRevert && line.line_type === "removed" && (
                  <Button
                    type="button"
                    variant="ghost"
                    size="xs"
                    onClick={handleRevert}
                    className="h-auto px-1.5 py-0.5 text-[11px]"
                    tooltip="Revert this change"
                    aria-label="Revert change"
                  >
                    ↺
                  </Button>
                )}
                <Button
                  type="button"
                  variant="ghost"
                  size="xs"
                  onClick={onClose}
                  className="h-auto px-1.5 py-0.5 text-[11px]"
                  tooltip="Close"
                  shortcut="escape"
                  aria-label="Close diff"
                >
                  ✕
                </Button>
              </div>
            )}
          </div>
        ))
      ) : (
        <div
          style={{
            height: `${lineHeight}px`,
            lineHeight: `${lineHeight}px`,
            fontSize: `${fontSize}px`,
            fontFamily,
            paddingLeft: "1rem",
            color: "var(--text-light, rgba(255, 255, 255, 0.5))",
            fontStyle: "italic",
            backgroundColor: "rgba(128, 128, 128, 0.1)",
          }}
        >
          No diff available
        </div>
      )}
    </div>
  );
}

InlineDiffComponent.displayName = "InlineDiff";

export const InlineDiff = memo(InlineDiffComponent);
