"use client";

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";

import { buttonClass } from "@/lib/ui/variants";

type ToastKind = "info" | "error" | "success";

interface ToastAction {
  label: string;
  onClick: () => void;
}

interface ToastOptions {
  action?: ToastAction;
  durationMs?: number;
}

interface ToastItem {
  id: number;
  message: string;
  kind: ToastKind;
  action?: ToastAction;
}

type ShowToast = (
  message: string,
  kind?: ToastKind,
  options?: ToastOptions,
) => void;

const ToastContext = createContext<ShowToast | null>(null);

// 普通提示 4s 自动消失；错误更需被看到，默认延长到 7s。
const TOAST_TTL_MS = 4000;
const ERROR_TOAST_TTL_MS = 7000;

// 屏读前缀：让无视觉的用户也能区分提示性质。
const SR_PREFIX: Record<ToastKind, string> = {
  error: "错误：",
  success: "成功：",
  info: "",
};

function toastShellClass(kind: ToastKind): string {
  if (kind === "error") return "border-conflict bg-panel text-conflict";
  if (kind === "success") return "border-pass bg-panel text-pass";
  return "border-line bg-panel text-ink";
}

export function ToastProvider({ children }: { children: ReactNode }) {
  const [items, setItems] = useState<ToastItem[]>([]);
  // 单调计数器作 id（不再用 Date.now()+random，避免极小概率碰撞且稳定可测）。
  const nextIdRef = useRef(0);
  // 在途定时器集合：卸载时统一清理，避免 setState-after-unmount 泄漏。
  const timersRef = useRef<Set<ReturnType<typeof setTimeout>>>(new Set());

  const dismiss = useCallback((id: number) => {
    setItems((prev) => prev.filter((t) => t.id !== id));
  }, []);

  const show = useCallback<ShowToast>((message, kind = "info", options) => {
    const id = nextIdRef.current++;
    setItems((prev) => [...prev, { id, message, kind, action: options?.action }]);
    const ttl =
      options?.durationMs ??
      (kind === "error" ? ERROR_TOAST_TTL_MS : TOAST_TTL_MS);
    const timer = setTimeout(() => {
      timersRef.current.delete(timer);
      setItems((prev) => prev.filter((t) => t.id !== id));
    }, ttl);
    timersRef.current.add(timer);
  }, []);

  // 卸载清理所有在途定时器。
  useEffect(() => {
    const timers = timersRef.current;
    return () => {
      timers.forEach((t) => clearTimeout(t));
      timers.clear();
    };
  }, []);

  const value = useMemo(() => show, [show]);

  const errorItems = items.filter((t) => t.kind === "error");
  const politeItems = items.filter((t) => t.kind !== "error");

  const renderItem = (t: ToastItem) => (
    <div
      key={t.id}
      className={`pointer-events-auto flex items-center gap-3 rounded border px-4 py-2 text-sm shadow-paper ${toastShellClass(
        t.kind,
      )}`}
    >
      <span className="min-w-0">
        <span className="sr-only">{SR_PREFIX[t.kind]}</span>
        {t.message}
      </span>
      {t.action ? (
        <button
          type="button"
          className={buttonClass({ variant: "ghost", size: "sm" })}
          onClick={() => {
            t.action?.onClick();
            dismiss(t.id);
          }}
        >
          {t.action.label}
        </button>
      ) : null}
    </div>
  );

  return (
    <ToastContext.Provider value={value}>
      {children}
      {/* error 用 assertive 立即播报；其余 info/success 用 polite 礼貌播报。两区共用同一锚点容器避免重叠。 */}
      <div className="pointer-events-none fixed bottom-6 right-6 z-50 flex flex-col gap-2">
        <div className="flex flex-col gap-2" aria-live="assertive" role="alert">
          {errorItems.map(renderItem)}
        </div>
        <div className="flex flex-col gap-2" aria-live="polite" role="status">
          {politeItems.map(renderItem)}
        </div>
      </div>
    </ToastContext.Provider>
  );
}

export function useToast(): ShowToast {
  const ctx = useContext(ToastContext);
  if (!ctx) {
    // 容错：未挂 Provider 时退化为 no-op（不应在生产路径发生）。
    return () => undefined;
  }
  return ctx;
}
