import { Button } from "@workspace/ui/components/button";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
} from "@workspace/ui/components/dialog";
import { Icon } from "@workspace/ui/composed/icon";
import {
  generateCaptcha,
  verifyCaptchaSlider,
} from "@workspace/ui/services/common/auth";
import { useCallback, useImperativeHandle, useRef, useState } from "react";
import { useTranslation } from "react-i18next";

export interface SliderCaptchaRef {
  reset: () => void;
}

interface SliderCaptchaProps {
  value?: string;
  onChange?: (value: string) => void;
}

interface TrailPoint {
  x: number;
  y: number;
  t: number;
}

const BLOCK_SIZE = 100;
const BG_NATURAL_WIDTH = 560;
const BG_NATURAL_HEIGHT = 280;

const SliderCaptcha = ({
  onChange,
  ref,
}: SliderCaptchaProps & { ref?: RefObject<SliderCaptchaRef | null> }) => {
  const { t } = useTranslation("auth");
  const containerRef = useRef<HTMLDivElement>(null);
  const [open, setOpen] = useState(false);
  const [captchaId, setCaptchaId] = useState("");
  const [bgImage, setBgImage] = useState("");
  const [blockImage, setBlockImage] = useState("");
  const [loading, setLoading] = useState(false);
  const [verified, setVerified] = useState(false);
  const [status, setStatus] = useState<"idle" | "success" | "fail">("idle");
  const [blockPos, setBlockPos] = useState({ x: 0, y: 50 });
  const [shaking, setShaking] = useState(false);

  const dragging = useRef(false);
  const startPointer = useRef({ x: 0, y: 0 });
  const startBlock = useRef({ x: 0, y: 50 });
  const dragStartTime = useRef(0);
  const trail = useRef<TrailPoint[]>([]);

  const getContainerSize = () => {
    const el = containerRef.current;
    if (!el) return { w: BG_NATURAL_WIDTH, h: BG_NATURAL_HEIGHT };
    return { w: el.clientWidth, h: el.clientHeight };
  };

  const fetchCaptcha = useCallback(async () => {
    setLoading(true);
    setStatus("idle");
    setBlockPos({ x: 0, y: 50 });
    trail.current = [];
    try {
      const res = await generateCaptcha();
      const data = res.data?.data;
      if (data) {
        setCaptchaId(data.id);
        setBgImage(data.image);
        setBlockImage(data.block_image ?? "");
      }
    } catch (e) {
      console.error("Failed to generate slider captcha:", e);
    } finally {
      setLoading(false);
    }
  }, []);

  const handleOpen = () => {
    if (verified) return;
    setOpen(true);
    fetchCaptcha();
  };

  useImperativeHandle(ref, () => ({
    reset: () => {
      setVerified(false);
      onChange?.("");
      setOpen(false);
      trail.current = [];
    },
  }));

  const onPointerDown = (e: React.PointerEvent) => {
    if (status === "success" || loading) return;
    dragging.current = true;
    dragStartTime.current = Date.now();
    trail.current = [];
    startPointer.current = { x: e.clientX, y: e.clientY };
    startBlock.current = { ...blockPos };
    (e.target as HTMLElement).setPointerCapture(e.pointerId);
    e.preventDefault();

    trail.current.push({
      x: Math.round(startBlock.current.x),
      y: Math.round(startBlock.current.y),
      t: 0,
    });
  };

  const onPointerMove = (e: React.PointerEvent) => {
    if (!dragging.current) return;
    const { w, h } = getContainerSize();
    const scaleX = BG_NATURAL_WIDTH / w;
    const scaleY = BG_NATURAL_HEIGHT / h;
    const dx = (e.clientX - startPointer.current.x) * scaleX;
    const dy = (e.clientY - startPointer.current.y) * scaleY;
    const newX = Math.max(
      0,
      Math.min(startBlock.current.x + dx, BG_NATURAL_WIDTH - BLOCK_SIZE)
    );
    const newY = Math.max(
      0,
      Math.min(startBlock.current.y + dy, BG_NATURAL_HEIGHT - BLOCK_SIZE)
    );
    setBlockPos({ x: newX, y: newY });

    trail.current.push({
      x: Math.round(newX),
      y: Math.round(newY),
      t: Date.now() - dragStartTime.current,
    });
  };

  const onPointerUp = async (e: React.PointerEvent) => {
    if (!dragging.current) return;
    dragging.current = false;
    const { w, h } = getContainerSize();
    const scaleX = BG_NATURAL_WIDTH / w;
    const scaleY = BG_NATURAL_HEIGHT / h;
    const dx = (e.clientX - startPointer.current.x) * scaleX;
    const dy = (e.clientY - startPointer.current.y) * scaleY;
    const finalX = Math.round(
      Math.max(
        0,
        Math.min(startBlock.current.x + dx, BG_NATURAL_WIDTH - BLOCK_SIZE)
      )
    );
    const finalY = Math.round(
      Math.max(
        0,
        Math.min(startBlock.current.y + dy, BG_NATURAL_HEIGHT - BLOCK_SIZE)
      )
    );
    setBlockPos({ x: finalX, y: finalY });

    trail.current.push({
      x: finalX,
      y: finalY,
      t: Date.now() - dragStartTime.current,
    });

    try {
      const res = await verifyCaptchaSlider({
        id: captchaId,
        x: finalX,
        y: finalY,
        trail: JSON.stringify(trail.current),
      });
      const token = res.data?.data?.token;
      if (token) {
        setStatus("success");
        setTimeout(() => {
          setVerified(true);
          onChange?.(token);
          setOpen(false);
        }, 600);
      } else {
        triggerFail();
      }
    } catch {
      triggerFail();
    }
  };

  const triggerFail = () => {
    setStatus("fail");
    setShaking(true);
    setTimeout(() => {
      setShaking(false);
      fetchCaptcha();
    }, 800);
  };

  const blockLeftPct = (blockPos.x / BG_NATURAL_WIDTH) * 100;
  const blockTopPct = (blockPos.y / BG_NATURAL_HEIGHT) * 100;
  const blockSizeWPct = (BLOCK_SIZE / BG_NATURAL_WIDTH) * 100;
  const blockSizeHPct = (BLOCK_SIZE / BG_NATURAL_HEIGHT) * 100;

  return (
    <>
      {/* Trigger button */}
      <button
        className={`relative flex w-full items-center gap-3 rounded-md border px-4 py-3 text-sm transition-colors ${
          verified
            ? "border-green-400 bg-green-50 text-green-700 dark:bg-green-950/30"
            : "border-input bg-background hover:bg-muted"
        }`}
        onClick={handleOpen}
        type="button"
      >
        <span
          className={`relative flex h-5 w-5 shrink-0 items-center justify-center rounded-full ${
            verified ? "bg-green-500" : "bg-primary"
          }`}
        >
          {verified ? (
            <Icon className="text-white text-xs" icon="mdi:check" />
          ) : (
            <>
              <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-60" />
              <span className="relative inline-flex h-3 w-3 rounded-full bg-primary" />
            </>
          )}
        </span>
        <span className={verified ? "font-medium" : "text-muted-foreground"}>
          {verified
            ? t("captcha.slider.success", "Verified")
            : t("captcha.slider.clickToVerify", "Click to verify")}
        </span>
        {verified && (
          <Icon className="ml-auto text-green-500" icon="mdi:check-circle" />
        )}
      </button>

      {/* Slider dialog */}
      <Dialog
        onOpenChange={(o) => {
          if (!o) setOpen(false);
        }}
        open={open}
      >
        <DialogContent className="select-none p-6 sm:max-w-md">
          <DialogHeader>
            <DialogTitle>
              {t("captcha.slider.title", "Security Verification")}
            </DialogTitle>
          </DialogHeader>

          <div
            className={`relative w-full overflow-hidden rounded-md bg-muted ${
              shaking ? "animate-[shake_0.4s_ease-in-out]" : ""
            }`}
            ref={containerRef}
            style={{
              paddingTop: `${(BG_NATURAL_HEIGHT / BG_NATURAL_WIDTH) * 100}%`,
            }}
          >
            <div className="absolute inset-0">
              {loading ? (
                <div className="flex h-full items-center justify-center">
                  <Icon className="animate-spin text-2xl" icon="mdi:loading" />
                </div>
              ) : bgImage ? (
                <>
                  <img
                    alt="captcha background"
                    className="absolute inset-0 h-full w-full"
                    draggable={false}
                    height={BG_NATURAL_HEIGHT}
                    src={bgImage}
                    width={BG_NATURAL_WIDTH}
                  />
                  {blockImage && (
                    <img
                      alt="captcha block"
                      className="absolute cursor-grab active:cursor-grabbing"
                      draggable={false}
                      height={BG_NATURAL_HEIGHT}
                      onPointerDown={onPointerDown}
                      onPointerMove={onPointerMove}
                      onPointerUp={onPointerUp}
                      src={blockImage}
                      style={{
                        filter:
                          status === "success"
                            ? "drop-shadow(0 0 3px rgba(74,222,128,0.9))"
                            : status === "fail"
                              ? "drop-shadow(0 0 3px rgba(248,113,113,0.9))"
                              : "drop-shadow(0 0 2px rgba(255,255,255,0.7))",
                        left: `${blockLeftPct}%`,
                        top: `${blockTopPct}%`,
                        width: `${blockSizeWPct}%`,
                        height: `${blockSizeHPct}%`,
                        touchAction: "none",
                      }}
                      width={BG_NATURAL_WIDTH}
                    />
                  )}
                  {status !== "idle" && (
                    <div
                      className={`absolute inset-0 flex items-center justify-center font-medium text-sm ${
                        status === "success"
                          ? "bg-green-500/20 text-green-700"
                          : "bg-red-500/20 text-red-700"
                      }`}
                    >
                      {status === "success"
                        ? t("captcha.slider.success", "Verified")
                        : t("captcha.slider.fail", "Try again")}
                    </div>
                  )}
                </>
              ) : null}
            </div>
          </div>

          <p className="text-center text-muted-foreground text-xs">
            {t("captcha.slider.hint", "Drag the piece to fit the puzzle")}
          </p>

          <Button
            className="w-full"
            disabled={loading}
            onClick={fetchCaptcha}
            size="sm"
            type="button"
            variant="ghost"
          >
            <Icon icon="mdi:refresh" />
            {t("captcha.clickToRefresh", "Refresh")}
          </Button>
        </DialogContent>
      </Dialog>

      <style>{`
          @keyframes shake {
            0%, 100% { transform: translateX(0); }
            20% { transform: translateX(-8px); }
            40% { transform: translateX(8px); }
            60% { transform: translateX(-6px); }
            80% { transform: translateX(6px); }
          }
        `}</style>
    </>
  );
};

SliderCaptcha.displayName = "SliderCaptcha";

export default SliderCaptcha;
