"use client";

/**
 * HandwritingPad — 手写板组件（ai14，P5）
 *
 * 基于 Canvas 实现手写作答能力，支持鼠标与触摸输入。
 * - 笔迹平滑（二次贝塞尔曲线）
 * - 可调笔触粗细与颜色
 * - 清空 / 撤销
 * - 导出为 PNG data URL（base64），供答案附件上传
 *
 * 用于论述题（essay）场景，学生可用手写板输入公式 / 证明过程。
 */

import {
  useCallback,
  useEffect,
  useRef,
  useState,
  type PointerEvent as ReactPointerEvent,
} from "react";
import { cn } from "@/lib/exam-types";

export interface HandwritingPadProps {
  /** 导出图片回调（base64 data URL） */
  onExport?: (dataUrl: string) => void;
  /** 笔触粗细（默认 3） */
  strokeWidth?: number;
  /** 画布宽度（默认 100%） */
  width?: number | string;
  /** 画布高度（默认 240） */
  height?: number;
  /** 画布 aria 标签 */
  ariaLabel?: string;
  /** 受控禁用 */
  disabled?: boolean;
}

interface Point {
  x: number;
  y: number;
}

interface Stroke {
  points: Point[];
  width: number;
}

/** 默认笔触颜色（使用 ink 令牌，避免硬编码） */
const PEN_COLOR = "var(--color-ink)";

/** 将 Pointer 坐标转换为画布相对坐标 */
function getRelativePoint(
  e: ReactPointerEvent<HTMLCanvasElement>,
  canvas: HTMLCanvasElement,
): Point {
  const rect = canvas.getBoundingClientRect();
  const scaleX = canvas.width / rect.width;
  const scaleY = canvas.height / rect.height;
  return {
    x: (e.clientX - rect.left) * scaleX,
    y: (e.clientY - rect.top) * scaleY,
  };
}

export function HandwritingPad({
  onExport,
  strokeWidth = 3,
  width = "100%",
  height = 240,
  ariaLabel = "手写作答区",
  disabled = false,
}: HandwritingPadProps): JSX.Element {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const [strokes, setStrokes] = useState<Stroke[]>([]);
  const [currentStroke, setCurrentStroke] = useState<Stroke | null>(null);
  const [isDrawing, setIsDrawing] = useState(false);

  /** 重绘画布 */
  const redraw = useCallback((): void => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.strokeStyle = PEN_COLOR;
    ctx.lineCap = "round";
    ctx.lineJoin = "round";

    const drawStroke = (stroke: Stroke): void => {
      if (stroke.points.length === 0) return;
      ctx.lineWidth = stroke.width;
      ctx.beginPath();
      const first = stroke.points[0];
      if (!first) return;
      ctx.moveTo(first.x, first.y);
      for (let i = 1; i < stroke.points.length; i++) {
        const prev = stroke.points[i - 1];
        const curr = stroke.points[i];
        if (!prev || !curr) continue;
        // 二次贝塞尔平滑
        const midX = (prev.x + curr.x) / 2;
        const midY = (prev.y + curr.y) / 2;
        ctx.quadraticCurveTo(prev.x, prev.y, midX, midY);
      }
      const last = stroke.points[stroke.points.length - 1];
      if (last) ctx.lineTo(last.x, last.y);
      ctx.stroke();
    };

    strokes.forEach(drawStroke);
    if (currentStroke) drawStroke(currentStroke);
  }, [strokes, currentStroke]);

  useEffect(() => {
    redraw();
  }, [redraw]);

  /** 导出当前画布为 PNG data URL */
  const exportImage = useCallback((): string => {
    const canvas = canvasRef.current;
    if (!canvas) return "";
    return canvas.toDataURL("image/png");
  }, []);

  // strokes 变化时自动导出
  useEffect(() => {
    if (onExport && strokes.length > 0) {
      onExport(exportImage());
    }
  }, [strokes, onExport, exportImage]);

  const handlePointerDown = useCallback(
    (e: ReactPointerEvent<HTMLCanvasElement>): void => {
      if (disabled) return;
      e.preventDefault();
      (e.target as HTMLCanvasElement).setPointerCapture(e.pointerId);
      const point = getRelativePoint(e, e.currentTarget);
      setIsDrawing(true);
      setCurrentStroke({ points: [point], width: strokeWidth });
    },
    [disabled, strokeWidth],
  );

  const handlePointerMove = useCallback(
    (e: ReactPointerEvent<HTMLCanvasElement>): void => {
      if (!isDrawing || disabled) return;
      e.preventDefault();
      const point = getRelativePoint(e, e.currentTarget);
      setCurrentStroke((prev) =>
        prev ? { ...prev, points: [...prev.points, point] } : prev,
      );
    },
    [isDrawing, disabled],
  );

  const handlePointerUp = useCallback(
    (e: ReactPointerEvent<HTMLCanvasElement>): void => {
      if (!isDrawing) return;
      e.preventDefault();
      (e.target as HTMLCanvasElement).releasePointerCapture(e.pointerId);
      setIsDrawing(false);
      setCurrentStroke((prev) => {
        if (prev) {
          setStrokes((s) => [...s, prev]);
        }
        return null;
      });
    },
    [isDrawing],
  );

  const handleClear = useCallback((): void => {
    setStrokes([]);
    setCurrentStroke(null);
    if (onExport) onExport("");
  }, [onExport]);

  const handleUndo = useCallback((): void => {
    setStrokes((s) => s.slice(0, -1));
  }, []);

  return (
    <div className="w-full" role="group" aria-label="手写板控件">
      <div
        className={cn(
          "rounded border border-rule bg-paper overflow-hidden",
          disabled && "opacity-60 pointer-events-none",
        )}
        style={{ background: "var(--color-paper)" }}
      >
        <canvas
          ref={canvasRef}
          width={800}
          height={height}
          style={{
            width,
            height,
            display: "block",
            touchAction: "none",
            cursor: disabled ? "not-allowed" : "crosshair",
          }}
          role="img"
          aria-label={ariaLabel}
          onPointerDown={handlePointerDown}
          onPointerMove={handlePointerMove}
          onPointerUp={handlePointerUp}
          onPointerCancel={handlePointerUp}
          onPointerLeave={handlePointerUp}
        />
      </div>
      <div className="mt-xs flex items-center gap-sm">
        <button
          type="button"
          onClick={handleUndo}
          disabled={strokes.length === 0 || disabled}
          className="btn-secondary text-xs"
          aria-label="撤销上一笔"
        >
          撤销
        </button>
        <button
          type="button"
          onClick={handleClear}
          disabled={(strokes.length === 0 && !currentStroke) || disabled}
          className="btn-secondary text-xs"
          aria-label="清空手写板"
        >
          清空
        </button>
        <span
          className="text-xs"
          style={{ color: "var(--color-ink-muted)" }}
          aria-live="polite"
        >
          {strokes.length} 笔
        </span>
      </div>
    </div>
  );
}
