import {
  CheckingOut,
  CreatingOrder,
  Failure,
  Processing,
  ReviewOrder,
  Succeed,
} from "@/components/billing/payment-checkout/order";
import { Preparing } from "@/components/billing/payment-checkout/preparing";
import {
  isPreparingState,
  type PaymentCheckoutState,
  PaymentCheckoutStep,
} from "@/components/billing/store";
import { useLocaleFunction } from "@/hooks/use-localed";
import { cn } from "@/lib/utils";
import { cva, type VariantProps } from "class-variance-authority";
import { motion } from "framer-motion";
import {
  CheckCircle2Icon,
  CircleDotIcon,
  CircleIcon,
  CircleXIcon,
} from "lucide-react";
import { type ReactNode, useCallback, useRef, useState } from "react";
import { useStore } from "zustand/react";
import type { StoreApi } from "zustand/vanilla";

export function PaymentCheckout({
  paymentCheckout,
}: {
  paymentCheckout: StoreApi<PaymentCheckoutState>;
}) {
  return (
    <div
      className="grid grid-cols-2 gap-4"
      style={{ gridTemplateColumns: "12rem auto" }}
    >
      <nav>
        <ol className="space-y-4 py-2">
          <PaymentCheckoutSteps paymentCheckout={paymentCheckout} />
        </ol>
      </nav>
      <Wrapper>
        <div className="p-2">
          <PaymentCheckoutContent paymentCheckout={paymentCheckout} />
        </div>
      </Wrapper>
    </div>
  );
}

function Wrapper({
  className,
  children,
}: {
  className?: string;
  children: ReactNode;
}) {
  const [height, setHeight] = useState<number | "auto">("auto");
  const resizeObserverRef = useRef<ResizeObserver | null>(null);

  const containerRef = useCallback((node: HTMLDivElement): void => {
    if (node !== null) {
      resizeObserverRef.current = new ResizeObserver((entries) => {
        // We only have one entry, so we can use entries[0].
        const observedHeight = entries?.[0]?.contentRect?.height;
        setHeight(observedHeight ?? "auto");
      });
      resizeObserverRef.current.observe(node);
    } else if (resizeObserverRef.current) {
      // Disconnect the observer when the node is unmounted to prevent memory leaks
      resizeObserverRef.current.disconnect();
    }
  }, []);

  return (
    <motion.div
      style={{ height }}
      animate={{ height }}
      transition={{
        duration: 0.2,
      }}
      className={cn("overflow-hidden ", className)}
    >
      <div ref={containerRef}>{children}</div>
    </motion.div>
  );
}

function PaymentCheckoutContent({
  paymentCheckout,
}: {
  paymentCheckout: StoreApi<PaymentCheckoutState>;
}) {
  const state = useStore(paymentCheckout);

  switch (state.step) {
    case PaymentCheckoutStep.completingBillingAddress:
    case PaymentCheckoutStep.selectingPaymentMethod:
      return <Preparing state={state} />;
    case PaymentCheckoutStep.creatingOrder:
      return <CreatingOrder state={state} />;
    case PaymentCheckoutStep.reviewingOrder:
      return <ReviewOrder state={state} />;
    case PaymentCheckoutStep.checkingOut:
      return <CheckingOut state={state} />;
    case PaymentCheckoutStep.processingAction:
      return <Processing state={state} />;
    case PaymentCheckoutStep.failure:
      return <Failure state={state} />;
    case PaymentCheckoutStep.succeed:
      return <Succeed state={state} />;
  }
}

function PaymentCheckoutSteps({
  paymentCheckout,
}: {
  paymentCheckout: StoreApi<PaymentCheckoutState>;
}) {
  const state = useStore(paymentCheckout);
  const t = useLocaleFunction();

  if (isPreparingState(state)) {
    return (
      <>
        <PaymentCheckoutStepItem
          title={t("payment.billing-details.complete.title")}
          description={t("payment.billing-details.complete.description")}
          variant={
            state.step === PaymentCheckoutStep.completingBillingAddress
              ? "current"
              : state.billingDetails != null
                ? "finished"
                : "unfinished"
          }
          onClick={() => state.completeBillingAddress()}
        />
        <PaymentCheckoutStepItem
          title={t("payment.payment-methods.select.title")}
          description={t("payment.payment-methods.select.description")}
          disabled={state.billingDetails == null}
          variant={
            state.step === PaymentCheckoutStep.selectingPaymentMethod
              ? "current"
              : state.paymentMethod != null
                ? "finished"
                : "unfinished"
          }
          onClick={() => state.selectPaymentMethod()}
        />
        <PaymentCheckoutStepItem
          title={t("payment.order.create.title")}
          description={t("payment.order.create.description")}
          variant="unfinished"
          disabled
        />
      </>
    );
  }

  return (
    <>
      <PaymentCheckoutStepItem
        title={t("payment.billing-details.complete.title")}
        description={t("payment.billing-details.complete.description")}
        variant="finished"
        disabled
      />
      <PaymentCheckoutStepItem
        title={t("payment.payment-methods.select.title")}
        description={t("payment.payment-methods.select.description")}
        variant="finished"
        disabled
      />
      <PaymentCheckoutStepItem
        title={t("payment.order.create.title")}
        description={t("payment.order.create.description")}
        variant={
          state.step === PaymentCheckoutStep.failure ? "failed" : "current"
        }
      />
    </>
  );
}

const itemVariants = cva(
  "font-normal text-start block space-y-1 disabled:cursor-not-allowed relative w-full group-last-of-type/step:before:hidden before:block before:absolute before:left-[7px] before:top-6 before:h-[calc(100%-10px)] before:w-0.5 before:bg-zinc-200",
  {
    variants: {
      variant: {
        finished: "before:bg-green-200",
        unfinished: "",
        current: "",
        failed: "",
      },
      state: {
        enabled: "",
        disabled: "opacity-70",
      },
    },
  }
);

const iconVariants = cva("size-4 transition-colors", {
  variants: {
    variant: {
      finished: "text-green-500",
      unfinished: "text-zinc-500",
      current: "text-primary",
      failed: "text-destructive",
    },
    state: {
      enabled: "",
      disabled: "",
    },
  },
});

const titleVariants = cva("font-normal text-sm", {
  variants: {
    variant: {
      finished: "text-green-500",
      unfinished: "text-zinc-500",
      current: "text-primary font-bold",
      failed: "text-destructive font-bold",
    },
    state: {
      enabled: "",
      disabled: "",
    },
  },
});

const descriptionVariants = cva("pl-6 text-sm text-muted-foreground", {
  variants: {
    variant: {
      finished: "text-green-500/50",
      unfinished: "",
      current: "",
      failed: "text-destructive/50",
    },
    state: {
      enabled: "",
      disabled: "",
    },
  },
});

function PaymentCheckoutStepItem({
  className,
  title,
  description,
  icon,
  variant,
  state,
  disabled,
  onClick,
}: {
  className?: string;
  title: ReactNode;
  description?: ReactNode;
  icon?: ReactNode;
  disabled?: boolean;
  onClick?: () => void;
} & VariantProps<typeof iconVariants>) {
  state = state ?? (disabled ? "disabled" : "enabled");
  const IconComponent =
    variant === "failed"
      ? CircleXIcon
      : variant === "finished"
        ? CheckCircle2Icon
        : variant === "current"
          ? CircleDotIcon
          : CircleIcon;

  return (
    <li className="group/step">
      <button
        className={cn(itemVariants({ variant, state }), className)}
        type="button"
        disabled={disabled}
        onClick={onClick}
      >
        <div className="flex items-center gap-2">
          {icon ?? (
            <IconComponent className={cn(iconVariants({ variant, state }))} />
          )}
          <h6 className={cn(titleVariants({ variant, state }))}>{title}</h6>
        </div>
        {description && (
          <p className={cn(descriptionVariants({ variant, state }))}>
            {description}
          </p>
        )}
      </button>
    </li>
  );
}
