"use client";
/**
 * Stripe Payment Element 弹窗
 *
 * 后端当前返回 PaymentIntent clientSecret，因此这里直接用它完成前端确认。
 */
import { useEffect, useId, useRef, useState, type FormEvent } from "react";
import {
  Elements,
  ExpressCheckoutElement,
  PaymentElement,
  useElements,
  useStripe,
} from "@stripe/react-stripe-js";
import {
  loadStripe,
  type AvailablePaymentMethods,
  type StripeExpressCheckoutElementConfirmEvent,
} from "@stripe/stripe-js";

import { ModalPortal } from "@/app/_components/core/modal-portal";
import { ExceptionHandler } from "@/core/errors";
import { ROUTES } from "@/router/routes";
import { Logger } from "@/utils/logger";
import { BrowserDetector } from "@/utils/browser-detect";
import { PlatformDetector } from "@/utils/platform-detect";
import { getStripeCustomerSessionClientSecret } from "@/lib/payment/payment_launch";
import { behaviorAnalytics } from "@/lib/analytics";

import { stripePaymentDialogStyles as styles } from "./stripe-payment-dialog.styles";

const log = new Logger("SubscriptionStripePaymentDialog");
const stripePublishableKey = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
const stripePromise = stripePublishableKey
  ? loadStripe(stripePublishableKey)
  : null;

type ExpressAvailabilityValue = boolean | { available: boolean } | undefined;

function isExpressMethodAvailable(value: ExpressAvailabilityValue): boolean {
  return value === true || (typeof value === "object" && value.available);
}

export interface StripePaymentDialogProps {
  clientSecret: string;
  customerSessionClientSecret?: string | null;
  savedPaymentMethodsEnabled?: boolean;
  returnPath?: string;
  onClose: () => void;
  onConfirmed?: () => void;
}

export function StripePaymentDialog({
  clientSecret,
  customerSessionClientSecret = null,
  savedPaymentMethodsEnabled = false,
  returnPath = ROUTES.subscription + "/success",
  onClose,
  onConfirmed,
}: StripePaymentDialogProps) {
  const titleId = useId();
  const descriptionId = useId();
  const savedCardClientSecret = getStripeCustomerSessionClientSecret({
    provider: "stripe",
    customerSessionClientSecret,
    savedPaymentMethodsEnabled,
  });

  if (!stripePromise) {
    return (
      <ModalPortal
        open
        persistent
        onClose={onClose}
        scrimClassName={styles.overlay}
        panelClassName={styles.dialog}
        scrimOpacity={0.5}
        role="alertdialog"
        ariaLabelledBy={titleId}
        ariaDescribedBy={descriptionId}
      >
        <div className={styles.header}>
          <h2 id={titleId} className={styles.title}>
            Payment unavailable
          </h2>
          <p id={descriptionId} className={styles.content}>
            Stripe publishable key is not configured for this build.
          </p>
        </div>
        <div className={styles.actions}>
          <button
            type="button"
            className={`${styles.button} ${styles.primary}`}
            onClick={onClose}
          >
            OK
          </button>
        </div>
      </ModalPortal>
    );
  }

  return (
    <ModalPortal
      open
      persistent
      onClose={onClose}
      scrimClassName={styles.overlay}
      panelClassName={styles.dialog}
      scrimOpacity={0.5}
      ariaLabelledBy={titleId}
      ariaDescribedBy={descriptionId}
    >
      <div className={styles.header}>
        <h2 id={titleId} className={styles.title}>
          Complete payment
        </h2>
        <p id={descriptionId} className={styles.content}>
          Enter your payment details securely through Stripe.
        </p>
      </div>
      <Elements
        key={clientSecret}
        stripe={stripePromise}
        options={{
          clientSecret,
          ...(savedCardClientSecret
            ? { customerSessionClientSecret: savedCardClientSecret }
            : {}),
          appearance: {
            theme: "stripe",
            variables: {
              borderRadius: "14px",
              colorPrimary: "#ff52a2",
              colorText: "#171717",
            },
          },
        }}
      >
        <StripePaymentForm
          returnPath={returnPath}
          onClose={onClose}
          onConfirmed={onConfirmed}
        />
      </Elements>
    </ModalPortal>
  );
}

function StripePaymentForm({
  returnPath,
  onClose,
  onConfirmed,
}: Pick<
  StripePaymentDialogProps,
  "returnPath" | "onClose" | "onConfirmed"
>) {
  const stripe = useStripe();
  const elements = useElements();
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const submittingRef = useRef(false);
  const [hasExpressPaymentMethods, setHasExpressPaymentMethods] = useState<
    boolean | null
  >(null);
  const [expressMaxColumns, setExpressMaxColumns] = useState(1);

  useEffect(() => {
    if (typeof window.matchMedia !== "function") return;
    const query = window.matchMedia("(min-width: 640px)");
    const updateColumns = () => setExpressMaxColumns(query.matches ? 2 : 1);
    updateColumns();
    query.addEventListener("change", updateColumns);
    return () => query.removeEventListener("change", updateColumns);
  }, []);

  const handleLoadError = (event: Parameters<
    NonNullable<React.ComponentProps<typeof PaymentElement>["onLoadError"]>
  >[0]) => {
    log.error("[stripe-payment-dialog] PaymentElement load failed", {
      type: event.error.type,
      code: event.error.code,
      message: event.error.message,
      declineCode: event.error.decline_code,
    });
    setErrorMessage(
      ExceptionHandler.message(
        event.error,
        "Payment methods could not be loaded. Please try again later.",
      ),
    );
  };

  const updateExpressAvailability = (
    availablePaymentMethods:
      | AvailablePaymentMethods
      | {
          applePay?: { available: boolean };
          googlePay?: { available: boolean };
          link?: { available: boolean };
          paypal?: { available: boolean };
          amazonPay?: { available: boolean };
          klarna?: { available: boolean };
        }
      | undefined,
  ) => {
    const availability = {
      applePay: isExpressMethodAvailable(availablePaymentMethods?.applePay),
      googlePay: isExpressMethodAvailable(availablePaymentMethods?.googlePay),
      link: isExpressMethodAvailable(availablePaymentMethods?.link),
      paypal: isExpressMethodAvailable(availablePaymentMethods?.paypal),
      amazonPay: isExpressMethodAvailable(availablePaymentMethods?.amazonPay),
      klarna: isExpressMethodAvailable(availablePaymentMethods?.klarna),
    };
    setHasExpressPaymentMethods(Object.values(availability).some(Boolean));
    const browser = BrowserDetector.isFacebookInAppBrowser()
        ? "facebook"
        : BrowserDetector.getBrowserName() || "unknown";
    const platform = PlatformDetector.getPlatform();
    const metadata = {
      browser,
      platform,
      availablePaymentMethods: availability,
    };
    log.info(
      "[stripe-payment-dialog] Express Checkout availability",
      metadata,
    );
    behaviorAnalytics.elementClick(
      "stripe.express_checkout_availability",
      "Stripe Express Checkout availability",
      metadata,
    );
  };

  const handleExpressLoadError = (event: Parameters<
    NonNullable<
      React.ComponentProps<typeof ExpressCheckoutElement>["onLoadError"]
    >
  >[0]) => {
    setHasExpressPaymentMethods(false);
    log.warn("[stripe-payment-dialog] Express Checkout load failed", {
      type: event.error.type,
      code: event.error.code,
      message: event.error.message,
    });
  };

  const confirmPayment = async (
    options: {
      submitPaymentElement: boolean;
      expressEvent?: StripeExpressCheckoutElementConfirmEvent;
    },
  ) => {
    if (!stripe || !elements || submittingRef.current) return;

    submittingRef.current = true;
    setIsSubmitting(true);
    setErrorMessage(null);

    if (options.submitPaymentElement) {
      let submitError: unknown;
      try {
        ({ error: submitError } = await elements.submit());
      } catch (error) {
        submitError = error;
      }
      if (submitError) {
        setErrorMessage(
          ExceptionHandler.message(
            submitError,
            "Please check your payment info.",
          ),
        );
        submittingRef.current = false;
        setIsSubmitting(false);
        return;
      }
    }

    const returnUrl = new URL(
      returnPath ?? ROUTES.subscription + "/success",
      window.location.origin,
    );
    let confirmationError: unknown;
    try {
      ({ error: confirmationError } = await stripe.confirmPayment({
        elements,
        confirmParams: {
          return_url: returnUrl.toString(),
        },
        redirect: "if_required",
      }));
    } catch (error) {
      confirmationError = error;
    }

    if (confirmationError) {
      const message = ExceptionHandler.message(
        confirmationError,
        "Payment confirmation failed.",
      );
      setErrorMessage(message);
      options.expressEvent?.paymentFailed({ reason: "fail", message });
      submittingRef.current = false;
      setIsSubmitting(false);
      return;
    }

    onConfirmed?.();
  };

  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    await confirmPayment({ submitPaymentElement: true });
  };

  return (
    <form className={styles.form} onSubmit={handleSubmit}>
      <section
        className={styles.expressSection}
        hidden={hasExpressPaymentMethods === false}
        aria-label="Express payment methods"
      >
        <ExpressCheckoutElement
          options={{
            paymentMethodOrder: [
              "apple_pay",
              "google_pay",
              "link",
              "paypal",
              "amazon_pay",
              "klarna",
            ],
            paymentMethods: {
              applePay: "always",
              googlePay: "always",
              link: "auto",
              paypal: "auto",
              amazonPay: "auto",
              klarna: "auto",
            },
            layout: {
              maxColumns: expressMaxColumns,
              maxRows: 6,
              overflow: "never",
            },
          }}
          onReady={(event) =>
            updateExpressAvailability(event.availablePaymentMethods)
          }
          onAvailablePaymentMethodsChange={(event) =>
            updateExpressAvailability(event.paymentMethods)
          }
          onLoadError={handleExpressLoadError}
          onConfirm={(event) =>
            void confirmPayment({
              submitPaymentElement: false,
              expressEvent: event,
            })
          }
        />
      </section>
      <section className={styles.cardSection} aria-labelledby="pay-by-card-title">
        <h3 id="pay-by-card-title" className={styles.cardTitle}>
          Pay by card
        </h3>
        <PaymentElement
          options={{
            layout: { type: "accordion", defaultCollapsed: true },
            paymentMethodOrder: ["card"],
            wallets: {
              applePay: "never",
              googlePay: "never",
              link: "never",
            },
          }}
          onLoadError={handleLoadError}
        />
      </section>
      {errorMessage ? (
        <p
          role="alert"
          className={styles.error}
        >
          {errorMessage}
        </p>
      ) : null}
      <div className={styles.actions}>
        <button
          type="button"
          className={`${styles.button} ${styles.secondary}`}
          onClick={onClose}
          disabled={isSubmitting}
        >
          Cancel
        </button>
        <button
          type="submit"
          className={`${styles.button} ${styles.primary}`}
          disabled={!stripe || !elements || isSubmitting}
        >
          {isSubmitting ? "Processing..." : "Pay now"}
        </button>
      </div>
    </form>
  );
}
