import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/shared/common/ui/button";
import { Input } from "@/shared/common/ui/input";
import { useTranslation } from "react-i18next";
import { Download } from "lucide-react";

const STAMP_WIDTH = 698;
// A three-line stamp renders at 698px x 161px. Height adjusts by line count.
const NAME_FONT_SIZE = 46;
const POSITION_FONT_SIZE = 40;
const TEXT_COLOR = "#0033cc";
const FONT_FAMILY = "Georgia, 'Times New Roman', serif";
const HORIZONTAL_PADDING = 8;
const MAX_TEXT_WIDTH = STAMP_WIDTH - HORIZONTAL_PADDING * 2;
// These spacing values are tuned to visually match the provided teeter reference.
// Baseline-to-baseline spacing (normal line spacing), NOT derived from font sizes.
const NAME_LINE_HEIGHT = 45;
const NAME_TO_POSITION_BASELINE_GAP = 45;
const POSITION_LINE_HEIGHT = 45;
// Gap between language blocks when user selects "both".
// Kept small so the second language starts with "normal" line spacing.
const BLOCK_GAP = 0;
const PADDING_TOP = 8;
const PADDING_BOTTOM = 8;
const TEXT_DESCENT_PADDING = 9;
const EXPORT_SCALE = 1;

export type StampBlock = {
  name: string;
  position: string;
};

type StampGeneratorProps = {
  blocks: StampBlock[];
  onPreviewChange?: (dataUrl: string | null) => void;
};

function measureTextWidth(
  text: string,
  fontSize: number,
  fontWeight: string,
): number {
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  if (!ctx) return text.length * fontSize * 0.5;
  ctx.font = `${fontWeight} ${fontSize}px ${FONT_FAMILY}`;
  return ctx.measureText(text).width;
}

function wrapWords(
  text: string,
  maxWidth: number,
  fontSize: number,
  fontWeight: string,
): string[] {
  const trimmed = text.trim();
  if (!trimmed) return [""];

  const words = trimmed.split(/\s+/);
  const lines: string[] = [];
  let currentLine = "";

  for (const word of words) {
    const candidate = currentLine ? `${currentLine} ${word}` : word;
    if (measureTextWidth(candidate, fontSize, fontWeight) <= maxWidth) {
      currentLine = candidate;
    } else {
      if (currentLine) lines.push(currentLine);
      currentLine = word;
    }
  }

  if (currentLine) lines.push(currentLine);
  return lines.length > 0 ? lines : [""];
}

function buildLayout(blocks: StampBlock[]) {
  const layoutBlocks = blocks.map((block) => {
    const nameLines = wrapWords(
      block.name,
      MAX_TEXT_WIDTH,
      NAME_FONT_SIZE,
      "bold",
    );
    const positionLines = wrapWords(
      block.position,
      MAX_TEXT_WIDTH,
      POSITION_FONT_SIZE,
      "bold",
    );
    const widestLine = Math.max(
      ...nameLines.map((line) => measureTextWidth(line, NAME_FONT_SIZE, "bold")),
      ...positionLines.map((line) =>
        measureTextWidth(line, POSITION_FONT_SIZE, "bold"),
      ),
    );

    return { nameLines, positionLines, widestLine };
  });
  const width = Math.min(
    STAMP_WIDTH,
    Math.max(
      1,
      Math.ceil(
        Math.max(...layoutBlocks.map((block) => block.widestLine)) +
          HORIZONTAL_PADDING * 2,
      ),
    ),
  );
  const centerX = width / 2;
  let nextNameBaseline = PADDING_TOP + NAME_FONT_SIZE;
  let lastTextBaseline = nextNameBaseline;
  const elements: React.ReactNode[] = [];

  layoutBlocks.forEach(({ nameLines, positionLines }, blockIndex) => {
    const nameY = nextNameBaseline;
    elements.push(
      <text
        key={`name-${blockIndex}`}
        x={centerX}
        y={nameY}
        textAnchor="middle"
        fontFamily={FONT_FAMILY}
        fontWeight="bold"
        fill={TEXT_COLOR}
        fontSize={NAME_FONT_SIZE}
      >
        {nameLines.map((line, lineIndex) => (
          <tspan
            key={`${blockIndex}-name-${lineIndex}`}
            x={centerX}
            dy={lineIndex === 0 ? 0 : NAME_LINE_HEIGHT}
          >
            {line || " "}
          </tspan>
        ))}
      </text>,
    );

    // Use normal line spacing from name baseline to position baseline.
    const positionStartY =
      nameY + (nameLines.length - 1) * NAME_LINE_HEIGHT +
      NAME_TO_POSITION_BASELINE_GAP;
    elements.push(
      <text
        key={`position-${blockIndex}`}
        x={centerX}
        y={positionStartY}
        textAnchor="middle"
        fontFamily={FONT_FAMILY}
        fontWeight="bold"
        fill={TEXT_COLOR}
        fontSize={POSITION_FONT_SIZE}
      >
        {positionLines.map((line, lineIndex) => (
          <tspan
            key={`${blockIndex}-pos-${lineIndex}`}
            x={centerX}
            dy={lineIndex === 0 ? 0 : POSITION_LINE_HEIGHT}
          >
            {line}
          </tspan>
        ))}
      </text>,
    );

    const lastPositionBaselineY =
      positionStartY + (positionLines.length - 1) * POSITION_LINE_HEIGHT;
    lastTextBaseline = lastPositionBaselineY;

    if (blockIndex < blocks.length - 1) {
      nextNameBaseline = lastPositionBaselineY + NAME_LINE_HEIGHT + BLOCK_GAP;
    }
  });

  // Fit-to-content height with small padding.
  const height = Math.max(
    1,
    lastTextBaseline + TEXT_DESCENT_PADDING + PADDING_BOTTOM,
  );

  return { elements, height, width };
}

async function svgToPngDataUrl(
  svgElement: SVGSVGElement,
  width: number,
  height: number,
): Promise<string> {
  const clone = svgElement.cloneNode(true) as SVGSVGElement;
  clone.setAttribute("width", String(width));
  clone.setAttribute("height", String(height));
  clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");

  const serializer = new XMLSerializer();
  const svgString = serializer.serializeToString(clone);
  const svgBlob = new Blob([svgString], {
    type: "image/svg+xml;charset=utf-8",
  });
  const url = URL.createObjectURL(svgBlob);

  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => {
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext("2d");
      if (!ctx) {
        URL.revokeObjectURL(url);
        reject(new Error("Canvas not supported"));
        return;
      }
      ctx.clearRect(0, 0, width, height);
      ctx.drawImage(img, 0, 0, width, height);
      URL.revokeObjectURL(url);
      resolve(canvas.toDataURL("image/png"));
    };
    img.onerror = () => {
      URL.revokeObjectURL(url);
      reject(new Error("Failed to render stamp image"));
    };
    img.src = url;
  });
}

const StampGenerator: React.FC<StampGeneratorProps> = ({
  blocks: initialBlocks,
  onPreviewChange,
}) => {
  const { t } = useTranslation();
  const svgRef = useRef<SVGSVGElement>(null);
  const [editableBlocks, setEditableBlocks] = useState<StampBlock[]>(initialBlocks);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);

  useEffect(() => {
    setEditableBlocks(initialBlocks);
  }, [initialBlocks]);

  const { elements, height, width } = useMemo(
    () => buildLayout(editableBlocks),
    [editableBlocks],
  );

  const updateBlock = (
    index: number,
    field: keyof StampBlock,
    value: string,
  ) => {
    setEditableBlocks((prev) =>
      prev.map((block, i) =>
        i === index ? { ...block, [field]: value } : block,
      ),
    );
  };

  const refreshPreview = useCallback(async () => {
    if (!svgRef.current) return;
    try {
      const dataUrl = await svgToPngDataUrl(
        svgRef.current,
        width * EXPORT_SCALE,
        height * EXPORT_SCALE,
      );
      setPreviewUrl(dataUrl);
      onPreviewChange?.(dataUrl);
    } catch {
      setPreviewUrl(null);
      onPreviewChange?.(null);
    }
  }, [height, onPreviewChange, width]);

  useEffect(() => {
    const timer = window.setTimeout(() => {
      void refreshPreview();
    }, 150);
    return () => window.clearTimeout(timer);
  }, [editableBlocks, height, refreshPreview]);

  const handleDownload = async () => {
    if (!svgRef.current) return;
    const dataUrl = await svgToPngDataUrl(
      svgRef.current,
      width * EXPORT_SCALE,
      height * EXPORT_SCALE,
    );
    const link = document.createElement("a");
    link.href = dataUrl;
    link.download = "stamp.png";
    link.click();
  };

  return (
    <div className="space-y-4 text-left">
      {editableBlocks.map((block, index) => (
        <div
          key={`stamp-block-${index}`}
          className="space-y-2 rounded-lg border border-gray-200 p-3 dark:border-gray-600"
        >
          {editableBlocks.length > 1 && (
            <p className="text-xs font-medium text-gray-500 dark:text-gray-400">
              {index === 0
                ? t("signatureUpload.stampBlockAmharic")
                : t("signatureUpload.stampBlockEnglish")}
            </p>
          )}
          <div className="space-y-1">
            <label className="text-sm font-medium dark:text-gray-200">
              {t("signatureUpload.stampNameLabel")}
            </label>
            <Input
              value={block.name}
              onChange={(e) => updateBlock(index, "name", e.target.value)}
              className="dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600"
            />
          </div>
          <div className="space-y-1">
            <label className="text-sm font-medium dark:text-gray-200">
              {t("signatureUpload.stampPositionLabel")}
            </label>
            <Input
              value={block.position}
              onChange={(e) => updateBlock(index, "position", e.target.value)}
              className="dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600"
            />
          </div>
        </div>
      ))}

      <div
        className="overflow-x-auto rounded-lg border border-dashed border-gray-300 p-2 dark:border-gray-600"
        style={{
          background:
            "repeating-conic-gradient(#e5e7eb 0% 25%, transparent 0% 50%) 0 0 / 16px 16px",
        }}
      >
        <svg
          ref={svgRef}
          width={width}
          height={height}
          viewBox={`0 0 ${width} ${height}`}
          xmlns="http://www.w3.org/2000/svg"
          className="mx-auto max-w-full"
          aria-label="Teeter stamp preview"
        >
          {elements}
        </svg>
      </div>

      {previewUrl && (
        <div className="space-y-2">
          <p className="text-sm text-gray-600 dark:text-gray-300">
            {t("signatureUpload.generatedTeeterPreview")}:
          </p>
          <div className="mx-auto w-full max-w-md rounded-lg border border-gray-200 p-4 dark:border-gray-600 dark:bg-gray-700">
            <img
              src={previewUrl}
              alt={t("signatureUpload.generatedTeeterPreview")}
              className="mx-auto max-h-40 max-w-full object-contain"
            />
          </div>
        </div>
      )}

      <Button
        type="button"
        onClick={() => void handleDownload()}
        className="w-full bg-blue-600 text-white hover:bg-blue-700"
      >
        <Download className="mr-2 h-4 w-4" />
        {t("signatureUpload.downloadTransparentPng")}
      </Button>
    </div>
  );
};

export default StampGenerator;
export { svgToPngDataUrl, STAMP_WIDTH };
