"use client";

/**
 * ImageUpload — 图片上传组件（ai14，P5）
 *
 * 支持拖拽与点击选择图片，多图预览，移除单张。
 * - 受控组件：value 为 data URL 数组，onChange 回传最新数组
 * - 限制：单张 ≤ 5MB，最多 maxCount 张，类型 image/*
 * - 预览：缩略图网格，点击可全屏查看
 *
 * 用于论述题（essay）附件上传场景。
 */

import {
  useCallback,
  useRef,
  useState,
  type ChangeEvent,
  type DragEvent,
} from "react";
import { cn } from "@/lib/exam-types";

export interface ImageUploadProps {
  /** 当前图片列表（data URL 数组） */
  value: string[];
  /** 变更回调 */
  onChange: (images: string[]) => void;
  /** 最大张数（默认 5） */
  maxCount?: number;
  /** 单张最大字节（默认 5MB） */
  maxSizeBytes?: number;
  /** 受控禁用 */
  disabled?: boolean;
  /** aria 标签 */
  ariaLabel?: string;
}

const DEFAULT_MAX_COUNT = 5;
const DEFAULT_MAX_SIZE = 5 * 1024 * 1024;

/** 将 File 转为 data URL */
function fileToDataUrl(file: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(reader.result as string);
    reader.onerror = () => reject(reader.error);
    reader.readAsDataURL(file);
  });
}

export function ImageUpload({
  value,
  onChange,
  maxCount = DEFAULT_MAX_COUNT,
  maxSizeBytes = DEFAULT_MAX_SIZE,
  disabled = false,
  ariaLabel = "图片上传",
}: ImageUploadProps): JSX.Element {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [previewIndex, setPreviewIndex] = useState<number | null>(null);

  const addFiles = useCallback(
    async (files: FileList | File[]): Promise<void> => {
      setError(null);
      const fileArray = Array.from(files).filter((f) =>
        f.type.startsWith("image/"),
      );
      if (fileArray.length === 0) {
        setError("请选择图片文件");
        return;
      }
      const remaining = maxCount - value.length;
      if (remaining <= 0) {
        setError(`最多上传 ${maxCount} 张图片`);
        return;
      }
      const toProcess = fileArray.slice(0, remaining);
      const oversized = toProcess.filter((f) => f.size > maxSizeBytes);
      if (oversized.length > 0) {
        setError(
          `单张图片不能超过 ${Math.round(maxSizeBytes / 1024 / 1024)}MB`,
        );
        return;
      }
      try {
        const dataUrls = await Promise.all(toProcess.map(fileToDataUrl));
        onChange([...value, ...dataUrls]);
      } catch {
        setError("图片读取失败，请重试");
      }
    },
    [value, onChange, maxCount, maxSizeBytes],
  );

  const handleFileChange = useCallback(
    (e: ChangeEvent<HTMLInputElement>): void => {
      if (!e.target.files) return;
      void addFiles(e.target.files);
      // 重置 input value 以支持重复选择同一文件
      e.target.value = "";
    },
    [addFiles],
  );

  const handleDrop = useCallback(
    (e: DragEvent<HTMLDivElement>): void => {
      e.preventDefault();
      setIsDragging(false);
      if (disabled) return;
      if (e.dataTransfer.files.length > 0) {
        void addFiles(e.dataTransfer.files);
      }
    },
    [addFiles, disabled],
  );

  const handleDragOver = useCallback(
    (e: DragEvent<HTMLDivElement>): void => {
      e.preventDefault();
      if (!disabled) setIsDragging(true);
    },
    [disabled],
  );

  const handleDragLeave = useCallback(
    (e: DragEvent<HTMLDivElement>): void => {
      e.preventDefault();
      setIsDragging(false);
    },
    [],
  );

  const handleRemove = useCallback(
    (index: number): void => {
      const next = value.filter((_, i) => i !== index);
      onChange(next);
    },
    [value, onChange],
  );

  const canAddMore = value.length < maxCount && !disabled;

  return (
    <div className="w-full" role="group" aria-label={ariaLabel}>
      {/* 拖拽区 */}
      {canAddMore && (
        <div
          role="button"
          tabIndex={0}
          aria-label="点击或拖拽上传图片"
          onClick={() => inputRef.current?.click()}
          onKeyDown={(e) => {
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              inputRef.current?.click();
            }
          }}
          onDrop={handleDrop}
          onDragOver={handleDragOver}
          onDragLeave={handleDragLeave}
          className={cn(
            "flex flex-col items-center justify-center gap-sm rounded border border-dashed py-lg cursor-pointer transition-colors",
            isDragging
              ? "border-accent bg-accent-muted"
              : "border-rule hover:border-accent",
          )}
          style={{ background: "var(--color-paper)" }}
        >
          <svg
            width="28"
            height="28"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            strokeWidth="1.5"
            aria-hidden="true"
            style={{ color: "var(--color-ink-muted)" }}
          >
            <path d="M12 16V4M8 8l4-4 4 4M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2" />
          </svg>
          <p
            className="text-xs"
            style={{ color: "var(--color-ink-muted)" }}
          >
            点击或拖拽图片到此处（最多 {maxCount} 张，单张 ≤{" "}
            {Math.round(maxSizeBytes / 1024 / 1024)}MB）
          </p>
          <input
            ref={inputRef}
            type="file"
            accept="image/*"
            multiple
            onChange={handleFileChange}
            disabled={disabled}
            className="sr-only"
            aria-label="选择图片文件"
          />
        </div>
      )}

      {/* 错误提示 */}
      {error && (
        <p
          className="mt-xs text-xs"
          style={{ color: "var(--color-danger)" }}
          role="alert"
        >
          {error}
        </p>
      )}

      {/* 缩略图网格 */}
      {value.length > 0 && (
        <ul className="mt-sm grid grid-cols-3 sm:grid-cols-5 gap-sm" aria-label="已上传图片列表">
          {value.map((img, i) => (
            <li
              key={i}
              className="relative group rounded border border-rule overflow-hidden"
              style={{ aspectRatio: "1 / 1" }}
            >
              <img
                src={img}
                alt={`已上传图片 ${i + 1}`}
                className="w-full h-full object-cover cursor-pointer"
                onClick={() => setPreviewIndex(i)}
                onKeyDown={(e) => {
                  if (e.key === "Enter" || e.key === " ") {
                    e.preventDefault();
                    setPreviewIndex(i);
                  }
                }}
                tabIndex={0}
                role="button"
                aria-label={`查看图片 ${i + 1} 大图`}
              />
              {!disabled && (
                <button
                  type="button"
                  onClick={() => handleRemove(i)}
                  className="absolute top-1 right-1 w-5 h-5 rounded-full flex items-center justify-center text-xs"
                  style={{
                    background: "rgba(0,0,0,0.6)",
                    color: "var(--color-paper)",
                  }}
                  aria-label={`移除图片 ${i + 1}`}
                >
                  ×
                </button>
              )}
            </li>
          ))}
        </ul>
      )}

      {/* 全屏预览 */}
      {previewIndex !== null && value[previewIndex] && (
        <div
          role="dialog"
          aria-modal="true"
          aria-label="图片预览"
          className="fixed inset-0 z-50 flex items-center justify-center p-lg"
          style={{ background: "rgba(0,0,0,0.8)" }}
          onClick={() => setPreviewIndex(null)}
          onKeyDown={(e) => {
            if (e.key === "Escape") setPreviewIndex(null);
          }}
          tabIndex={-1}
        >
          <img
            src={value[previewIndex]}
            alt={`图片预览 ${previewIndex + 1}`}
            className="max-w-full max-h-full object-contain"
          />
        </div>
      )}
    </div>
  );
}
