"use client";

import { CheckIcon, XIcon } from "@/assets/icons";
import { cn } from "@/lib/utils";
import gsap from "gsap";
import {
  forwardRef,
  useId,
  useImperativeHandle,
  useLayoutEffect,
  useRef,
  type InputHTMLAttributes,
} from "react";

type PropsType = {
  withIcon?: "check" | "x" | "none";
  withBg?: boolean;
  label: string;
  name?: string;
  minimal?: boolean;
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
  radius?: "default" | "md";
  className?: string;
} & Omit<InputHTMLAttributes<HTMLInputElement>, "type">;

function prefersReducedMotion() {
  return (
    typeof window !== "undefined" &&
    window.matchMedia("(prefers-reduced-motion: reduce)").matches
  );
}

export const Checkbox = forwardRef<HTMLInputElement, PropsType>(function Checkbox(
  {
    withIcon = "check",
    label,
    name,
    withBg = true,
    minimal,
    onChange,
    radius = "md",
    className,
    disabled,
    id: idProp,
    checked,
    defaultChecked,
    ...rest
  },
  ref,
) {
  const autoId = useId();
  const id = idProp ?? autoId;
  const inputRef = useRef<HTMLInputElement>(null);
  const boxRef = useRef<HTMLDivElement>(null);
  const fillRef = useRef<HTMLDivElement>(null);
  const iconRef = useRef<HTMLSpanElement>(null);
  const glowRef = useRef<HTMLDivElement>(null);

  useImperativeHandle(ref, () => inputRef.current as HTMLInputElement);

  const animateState = (isChecked: boolean) => {
    const box = boxRef.current;
    if (!box || prefersReducedMotion()) return;

    const fill = fillRef.current;
    const icon = iconRef.current;
    const glow = glowRef.current;

    gsap.killTweensOf([box, fill, icon, glow].filter(Boolean));

    if (isChecked) {
      const tl = gsap.timeline();
      tl.to(box, { scale: 1.14, duration: 0.1, ease: "power2.out" }).to(box, {
        scale: 1,
        duration: 0.4,
        ease: "back.out(2.6)",
      });
      if (fill) {
        tl.fromTo(
          fill,
          { scale: 0.35, opacity: 0 },
          { scale: 1, opacity: 1, duration: 0.32, ease: "power3.out" },
          0,
        );
      }
      if (glow) {
        tl.fromTo(
          glow,
          { opacity: 0, scale: 0.75 },
          { opacity: 1, scale: 1, duration: 0.38, ease: "power2.out" },
          0.04,
        );
      }
      if (icon) {
        tl.fromTo(
          icon,
          { scale: 0, rotate: -48, opacity: 0 },
          {
            scale: 1,
            rotate: 0,
            opacity: 1,
            duration: 0.38,
            ease: "back.out(3)",
          },
          0.1,
        );
      }
    } else {
      gsap.to(box, { scale: 1, duration: 0.22, ease: "power2.out" });
      if (fill) {
        gsap.to(fill, {
          scale: 0.4,
          opacity: 0,
          duration: 0.2,
          ease: "power2.in",
        });
      }
      if (glow) {
        gsap.to(glow, { opacity: 0, scale: 0.85, duration: 0.2 });
      }
      if (icon) {
        gsap.to(icon, {
          scale: 0,
          rotate: 36,
          opacity: 0,
          duration: 0.18,
          ease: "power2.in",
        });
      }
    }
  };

  useLayoutEffect(() => {
    if (inputRef.current) {
      animateState(inputRef.current.checked);
    }
  }, [checked]);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    animateState(e.target.checked);
    onChange?.(e);
  };

  const showCheckIcon = withIcon === "check";
  const showXIcon = withIcon === "x";
  const showDot = withIcon === "none";

  return (
    <label
      htmlFor={id}
      className={cn(
        "group flex cursor-pointer select-none items-center",
        disabled && "cursor-not-allowed opacity-55",
        className,
      )}
    >
      <div className="relative shrink-0">
        <input
          ref={inputRef}
          type="checkbox"
          id={id}
          name={name}
          disabled={disabled}
          checked={checked}
          defaultChecked={defaultChecked}
          onChange={handleChange}
          className="peer sr-only"
          {...rest}
        />

        <div
          ref={boxRef}
          className={cn(
            "relative mr-2.5 flex size-5 items-center justify-center overflow-hidden border-2 shadow-theme-xs backdrop-blur-sm transition-[border-color,box-shadow] duration-200",
            "border-stroke/80 bg-white/90 dark:border-dark-3 dark:bg-dark-2/90",
            "peer-checked:border-primary peer-checked:shadow-[0_0_0_3px_rgba(87,80,241,0.18)]",
            "peer-focus-visible:ring-2 peer-focus-visible:ring-primary/45 peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-white dark:peer-focus-visible:ring-offset-gray-dark",
            "group-hover:border-primary/45 dark:group-hover:border-primary/55",
            radius === "md" ? "rounded-lg" : "rounded",
            minimal && "mr-3",
          )}
        >
          <div
            ref={glowRef}
            aria-hidden
            className="pointer-events-none absolute inset-0 opacity-0"
            style={{
              background:
                "radial-gradient(circle at 28% 22%, rgba(87,80,241,0.5), transparent 68%)",
            }}
          />

          {(withBg || showCheckIcon || showXIcon) && (
            <div
              ref={fillRef}
              aria-hidden
              className="absolute inset-0 origin-center scale-[0.35] bg-gradient-to-br from-primary via-primary/90 to-[#7B6CFF] opacity-0"
            />
          )}

          {showDot && (
            <span
              ref={iconRef}
              className="relative z-[1] block size-2.5 scale-0 rounded-sm bg-primary opacity-0 peer-checked:scale-100 peer-checked:opacity-100"
            />
          )}

          {showCheckIcon && (
            <span
              ref={iconRef}
              className="relative z-[1] inline-flex scale-0 opacity-0"
            >
              <CheckIcon className="size-3 text-white drop-shadow-sm" />
            </span>
          )}

          {showXIcon && (
            <span
              ref={iconRef}
              className="relative z-[1] inline-flex scale-0 opacity-0"
            >
              <XIcon className="size-3 text-white drop-shadow-sm" />
            </span>
          )}
        </div>
      </div>

      <span
        className={cn(
          !minimal && "text-body-sm font-medium",
          "text-dark transition-colors duration-200 group-hover:text-primary dark:text-white dark:group-hover:text-primary/90",
        )}
      >
        {label}
      </span>
    </label>
  );
});
