import { useCallback, useRef, useState } from "react";
import type React from "react";

type ToolType = "brush" | "eraser";

interface UseCanvasDrawingOptions {
  baseImage: string | null;
  brushSize: number;
  activeTool: ToolType;
}

const MASK_PREVIEW_COLOR = "rgba(129, 236, 255, 0.48)";
const MASK_EXPORT_COLOR = "rgba(13, 148, 136, 0.88)";

type CanvasPoint = { x: number; y: number };

function clamp(value: number, min: number, max: number): number {
  return Math.max(min, Math.min(max, value));
}

function drawMaskSegment(
  context: CanvasRenderingContext2D,
  from: CanvasPoint,
  to: CanvasPoint,
  lineWidth: number,
  tool: ToolType,
) {
  context.save();
  context.lineCap = "round";
  context.lineJoin = "round";
  context.lineWidth = lineWidth;
  context.globalCompositeOperation = tool === "brush" ? "source-over" : "destination-out";
  context.strokeStyle = tool === "brush" ? MASK_EXPORT_COLOR : "#000000";
  context.fillStyle = context.strokeStyle;
  context.beginPath();
  context.moveTo(from.x, from.y);
  context.lineTo(to.x, to.y);
  context.stroke();
  context.beginPath();
  context.arc(to.x, to.y, lineWidth / 2, 0, Math.PI * 2);
  context.fill();
  context.restore();
}

function getExportFeatherRadius(width: number, height: number): number {
  const radius = Math.round(Math.min(width, height) * 0.004);
  return clamp(radius, 2, 18);
}

function createExportMaskCanvas(
  maskCanvas: HTMLCanvasElement,
  width: number,
  height: number,
): HTMLCanvasElement | null {
  const normalized = document.createElement("canvas");
  normalized.width = width;
  normalized.height = height;
  const nCtx = normalized.getContext("2d");
  if (!nCtx) return null;
  nCtx.drawImage(maskCanvas, 0, 0, width, height);

  const out = document.createElement("canvas");
  out.width = width;
  out.height = height;
  const oCtx = out.getContext("2d");
  if (!oCtx) return null;

  oCtx.fillStyle = "#000000";
  oCtx.fillRect(0, 0, width, height);
  const feather = getExportFeatherRadius(width, height);
  if (feather > 0) {
    oCtx.save();
    oCtx.filter = `blur(${feather}px)`;
    oCtx.globalAlpha = 0.72;
    oCtx.drawImage(normalized, 0, 0);
    oCtx.restore();
  }
  oCtx.globalAlpha = 1;
  oCtx.filter = "none";
  oCtx.drawImage(normalized, 0, 0);
  return out;
}

function loadImage(src: string): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.onload = () => resolve(img);
    img.onerror = () => reject(new Error("图片加载失败"));
    img.src = src;
  });
}

export function useCanvasDrawing({ baseImage, brushSize, activeTool }: UseCanvasDrawingOptions) {
  const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
  const [sourceSize, setSourceSize] = useState({ width: 0, height: 0 });
  const [hasMask, setHasMask] = useState(false);
  const [isDrawing, setIsDrawing] = useState(false);

  const originalImgRef = useRef<HTMLImageElement | null>(null);
  const maskCanvasRef = useRef<HTMLCanvasElement | null>(null);
  const overlayRef = useRef<HTMLCanvasElement | null>(null);
  const lastPointRef = useRef<CanvasPoint | null>(null);
  const hasVisibleRef = useRef(false);
  const loadTokenRef = useRef(0);
  const metricsRef = useRef({ srcW: 0, srcH: 0, dispW: 0, dispH: 0 });

  const ensureMaskCanvas = useCallback((w: number, h: number) => {
    if (!maskCanvasRef.current) maskCanvasRef.current = document.createElement("canvas");
    const c = maskCanvasRef.current;
    if (c.width !== w) c.width = w;
    if (c.height !== h) c.height = h;
    return c;
  }, []);

  const renderComposite = useCallback((canvas: HTMLCanvasElement | null) => {
    if (!canvas || !originalImgRef.current) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(originalImgRef.current, 0, 0, canvas.width, canvas.height);

    const mask = maskCanvasRef.current;
    if (!mask || !hasVisibleRef.current) return;

    if (!overlayRef.current) overlayRef.current = document.createElement("canvas");
    const ov = overlayRef.current;
    if (ov.width !== canvas.width) ov.width = canvas.width;
    if (ov.height !== canvas.height) ov.height = canvas.height;
    const oCtx = ov.getContext("2d");
    if (!oCtx) return;
    oCtx.clearRect(0, 0, ov.width, ov.height);
    oCtx.drawImage(mask, 0, 0, ov.width, ov.height);
    oCtx.globalCompositeOperation = "source-in";
    oCtx.fillStyle = MASK_PREVIEW_COLOR;
    oCtx.fillRect(0, 0, ov.width, ov.height);
    oCtx.globalCompositeOperation = "source-over";
    ctx.drawImage(ov, 0, 0, canvas.width, canvas.height);
  }, []);

  const initCanvas = useCallback((canvas: HTMLCanvasElement | null) => {
    if (!baseImage || !canvas) return;
    const token = ++loadTokenRef.current;
    loadImage(baseImage).then((img) => {
      if (loadTokenRef.current !== token) return;
      originalImgRef.current = img;
      const srcW = img.naturalWidth || img.width;
      const srcH = img.naturalHeight || img.height;
      const maxW = 800, maxH = 500;
      let dW = srcW, dH = srcH;
      if (dW > maxW || dH > maxH) {
        const r = Math.min(maxW / dW, maxH / dH);
        dW = Math.round(dW * r);
        dH = Math.round(dH * r);
      }
      metricsRef.current = { srcW, srcH, dispW: dW, dispH: dH };
      setSourceSize({ width: srcW, height: srcH });
      setImageSize({ width: dW, height: dH });
      canvas.width = dW;
      canvas.height = dH;
      ensureMaskCanvas(srcW, srcH);
      hasVisibleRef.current = false;
      setHasMask(false);
      renderComposite(canvas);
    }).catch(() => {});
  }, [baseImage, ensureMaskCanvas, renderComposite]);

  const resolveMaskPoint = useCallback((canvas: HTMLCanvasElement, clientX: number, clientY: number) => {
    const rect = canvas.getBoundingClientRect();
    const scaleX = rect.width > 0 ? canvas.width / rect.width : 1;
    const scaleY = rect.height > 0 ? canvas.height / rect.height : 1;
    const dispX = clamp((clientX - rect.left) * scaleX, 0, canvas.width);
    const dispY = clamp((clientY - rect.top) * scaleY, 0, canvas.height);
    const mask = maskCanvasRef.current;
    const mScaleX = mask ? mask.width / Math.max(1, canvas.width) : 1;
    const mScaleY = mask ? mask.height / Math.max(1, canvas.height) : 1;
    return {
      point: { x: dispX * mScaleX, y: dispY * mScaleY },
      brushScale: Math.max(mScaleX, mScaleY),
    };
  }, []);

  const paintAt = useCallback((canvas: HTMLCanvasElement, clientX: number, clientY: number) => {
    const { srcW, srcH } = metricsRef.current;
    const mask = ensureMaskCanvas(srcW, srcH);
    const ctx = mask.getContext("2d", { willReadFrequently: true });
    if (!ctx) return;
    const { point, brushScale } = resolveMaskPoint(canvas, clientX, clientY);
    const prev = lastPointRef.current || point;
    const naturalBrush = Math.max(1, brushSize * brushScale);
    drawMaskSegment(ctx, prev, point, naturalBrush, activeTool);
    if (activeTool === "brush") { hasVisibleRef.current = true; setHasMask(true); }
    lastPointRef.current = point;
    renderComposite(canvas);
  }, [activeTool, brushSize, ensureMaskCanvas, renderComposite, resolveMaskPoint]);

  const startDrawing = useCallback((e: React.MouseEvent<HTMLCanvasElement>) => {
    e.preventDefault();
    setIsDrawing(true);
    paintAt(e.currentTarget, e.clientX, e.clientY);
  }, [paintAt]);

  const draw = useCallback((e: React.MouseEvent<HTMLCanvasElement>) => {
    if (!isDrawing) return;
    e.preventDefault();
    paintAt(e.currentTarget, e.clientX, e.clientY);
  }, [isDrawing, paintAt]);

  const finishDrawing = useCallback((canvas: HTMLCanvasElement | null) => {
    if (!isDrawing) { lastPointRef.current = null; setIsDrawing(false); return; }
    setIsDrawing(false);
    lastPointRef.current = null;
    if (canvas) renderComposite(canvas);
  }, [isDrawing, renderComposite]);

  const stopDrawing = useCallback((e: React.MouseEvent<HTMLCanvasElement>) => {
    finishDrawing(e.currentTarget);
  }, [finishDrawing]);

  const handleTouchStart = useCallback((e: React.TouchEvent<HTMLCanvasElement>) => {
    if (e.touches.length !== 1) return;
    e.preventDefault();
    setIsDrawing(true);
    paintAt(e.currentTarget, e.touches[0].clientX, e.touches[0].clientY);
  }, [paintAt]);

  const handleTouchMove = useCallback((e: React.TouchEvent<HTMLCanvasElement>) => {
    if (!isDrawing || e.touches.length !== 1) return;
    e.preventDefault();
    paintAt(e.currentTarget, e.touches[0].clientX, e.touches[0].clientY);
  }, [isDrawing, paintAt]);

  const handleTouchEnd = useCallback((e: React.TouchEvent<HTMLCanvasElement>) => {
    finishDrawing(e.currentTarget);
  }, [finishDrawing]);

  const clearMask = useCallback((canvas: HTMLCanvasElement | null) => {
    const mask = maskCanvasRef.current;
    if (mask) {
      const ctx = mask.getContext("2d");
      if (ctx) ctx.clearRect(0, 0, mask.width, mask.height);
    }
    hasVisibleRef.current = false;
    setHasMask(false);
    lastPointRef.current = null;
    if (canvas) renderComposite(canvas);
  }, [renderComposite]);

  const exportMaskDataUrl = useCallback((): string | null => {
    const mask = maskCanvasRef.current;
    if (!mask || !hasVisibleRef.current) return null;
    const { srcW, srcH } = metricsRef.current;
    const exportCanvas = createExportMaskCanvas(mask, srcW, srcH);
    return (exportCanvas || mask).toDataURL("image/png");
  }, []);

  return {
    imageSize,
    sourceSize,
    hasMask,
    isDrawing,
    initCanvas,
    renderComposite,
    startDrawing,
    draw,
    stopDrawing,
    handleTouchStart,
    handleTouchMove,
    handleTouchEnd,
    clearMask,
    exportMaskDataUrl,
  };
}
