"use client";

import Link from "next/link";
import {
  type ButtonHTMLAttributes,
  type ReactNode,
  useEffect,
  useId,
  useState,
} from "react";
import { GoArrowRight } from "react-icons/go";
import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider";
import { LoadingThreeDot } from "./loading-three-dot";

type ButtonVariant =
  | "default"
  | "secondary"
  | "countdown"
  | "outlined"
  | "dark";
type ArrowDirection = "left" | "right";

export type ButtonProps = Omit<
  ButtonHTMLAttributes<HTMLButtonElement>,
  "children"
> & {
  children: ReactNode;
  description?: string;
  variant?: ButtonVariant;
  arrowDirection?: ArrowDirection;
  countdownSeconds?: number;
  href?: string;
  isLoading?: boolean;
};

const FILLED_STROKE = "#FFFFFF";
const EMPTY_STROKE = "rgba(255, 255, 255, 0.5)";
const RADIUS = 18;
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;

export function Button({
  children,
  description,
  variant = "default",
  arrowDirection,
  countdownSeconds = 0,
  disabled,
  isLoading = false,
  href,
  className,
  type = "button",
  ...props
}: ButtonProps) {
  const { locale } = useI18n();
  const countdownId = useId();
  const isCountdown = variant === "countdown";
  const isOutlined = variant === "outlined";
  const initialCountdown = Math.max(0, Math.ceil(countdownSeconds));
  const [remainingSeconds, setRemainingSeconds] = useState(initialCountdown);
  const [animatedProgress, setAnimatedProgress] = useState(
    isCountdown && initialCountdown > 0 ? 0 : 1,
  );

  useEffect(() => {
    setRemainingSeconds(initialCountdown);
  }, [initialCountdown]);

  useEffect(() => {
    if (!isCountdown || remainingSeconds <= 0) {
      return;
    }

    const timeoutId = window.setTimeout(() => {
      setRemainingSeconds((current) => Math.max(0, current - 1));
    }, 1000);

    return () => window.clearTimeout(timeoutId);
  }, [isCountdown, remainingSeconds]);

  useEffect(() => {
    if (!isCountdown || initialCountdown <= 0) {
      setAnimatedProgress(1);
      return;
    }

    let animationFrameId = 0;
    const startedAt = window.performance.now();
    const duration = initialCountdown * 1000;

    setAnimatedProgress(0);

    const updateProgress = (currentTime: number) => {
      const elapsed = currentTime - startedAt;
      const nextProgress = Math.min(1, elapsed / duration);

      setAnimatedProgress(nextProgress);

      if (nextProgress < 1) {
        animationFrameId = window.requestAnimationFrame(updateProgress);
      }
    };

    animationFrameId = window.requestAnimationFrame(updateProgress);

    return () => window.cancelAnimationFrame(animationFrameId);
  }, [initialCountdown, isCountdown]);

  const countdownLocked = isCountdown && remainingSeconds > 0;
  const isDisabled = disabled || countdownLocked || isLoading;
  const progress = isCountdown ? animatedProgress : 1;

  const widthClass = variant === "secondary" ? "w-1/2" : "w-full";
  const baseClassName = [
    "inline-flex",
    widthClass,
    "items-center",
    "justify-center",
    "h-[52px]",
    variant === "dark" ? "rounded-[9px]" : "rounded-[11px]",
    "px-4",
    "text-center",
    "transition-opacity",
    variant === "outlined"
      ? "border border-[#8B8B8B] bg-transparent text-[#8B8B8B]"
      : variant === "dark"
        ? "border-none bg-[#2B2C31] text-white shadow-none hover:opacity-90"
        : "bg-linear-to-tl from-[#FE6F82] to-[#E03950] text-white",
    isDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
    className,
  ]
    .filter(Boolean)
    .join(" ");

  const renderArrow = (direction: ArrowDirection) => {
    if (arrowDirection !== direction) {
      return null;
    }

    return (
      <GoArrowRight
        aria-hidden="true"
        className={[
          "size-5",
          direction === "left"
            ? "rotate-180 rtl:rotate-0"
            : "rotate-0 rtl:rotate-180",
          isOutlined ? "text-[#8B8B8B]" : "text-white",
        ]
          .filter(Boolean)
          .join(" ")}
      />
    );
  };

  const button = (
    <button
      {...props}
      type={type}
      disabled={isDisabled}
      aria-describedby={description && !isOutlined ? countdownId : undefined}
      className={baseClassName}
    >
      {isLoading ? (
        <LoadingThreeDot />
      ) : (
        <span className="flex w-full items-center justify-center gap-2">
          {renderArrow("left")}
          <span className="flex min-w-0 flex-col items-center justify-center">
            <span className="flex items-center justify-center gap-2 text-center group-16 font-semibold leading-none">
              {children}
            </span>
            {description && !isOutlined ? (
              <span
                id={countdownId}
                className="mt-1 text-center group-10 font-semibold leading-none text-white"
              >
                {description}
              </span>
            ) : null}
          </span>
          {countdownLocked ? (
            <span className="shrink-0">
              <CountdownProgress value={remainingSeconds} progress={progress} />
            </span>
          ) : (
            renderArrow("right")
          )}
        </span>
      )}
    </button>
  );

  return href ? (
    <Link href={localizePath(href, locale)}>{button}</Link>
  ) : (
    button
  );
}

type CountdownProgressProps = {
  value: number;
  progress: number;
};

function CountdownProgress({ value, progress }: CountdownProgressProps) {
  const dashOffset = CIRCUMFERENCE * (1 - progress);

  return (
    <span className="relative flex h-[27px] w-[27px] items-center justify-center">
      <svg
        aria-hidden="true"
        className="-rotate-90"
        viewBox="0 0 44 44"
        width="27"
        height="27"
      >
        <circle
          cx="22"
          cy="22"
          r={RADIUS}
          fill="none"
          stroke={EMPTY_STROKE}
          strokeWidth="4"
        />
        <circle
          cx="22"
          cy="22"
          r={RADIUS}
          fill="none"
          stroke={FILLED_STROKE}
          strokeWidth="4"
          strokeLinecap="round"
          strokeDasharray={CIRCUMFERENCE}
          strokeDashoffset={dashOffset}
        />
      </svg>
      <span className="absolute inset-0 flex items-center justify-center group-12 font-semibold leading-none text-white">
        {value}
      </span>
    </span>
  );
}

export default Button;
