"use client";

import { useState } from "react";
import { useRouter } from "next/navigation";
import { loadStripe, type Stripe } from "@stripe/stripe-js";
import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";

// Loaded once at module scope (not per-render) — same reasoning as any
// other client-side SDK singleton. Never called at all in test mode
// (mounted conditionally below), so an unset publishable key there is
// harmless.
let stripePromise: Promise<Stripe | null> | null = null;
function getStripe(): Promise<Stripe | null> {
  if (!stripePromise) {
    stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY || "");
  }
  return stripePromise;
}

type Props = {
  clientSecret: string;
  orderNumber: string;
  orderId: number;
  testMode: boolean;
  /** Only present in test mode — see api/checkout/route.ts's own comment. */
  providerReference?: string;
  /** Where /checkout/verarbeitung sends the customer once payment is
   * confirmed — "checkout" (default) clears the cart/draft and lands on
   * /bestellbestaetigung, exactly like today. "account" is used by the
   * account order detail page's "Zahlungsart ändern" flow (an existing,
   * already-confirmed order — nothing to clear, and /bestellbestaetigung
   * would be the wrong destination): lands back on that same order's
   * page instead. See VerarbeitungContent.tsx's own branching on this. */
  returnContext?: "checkout" | "account";
};

// Rendered by CheckoutContent once /api/checkout returns
// `requiresPayment: true` (Kreditkarte/PayPal) — see
// spicy-leaping-pizza.md §3/§7. The order already exists in Payload at
// this point (status 'pending_payment'); this step only collects/confirms
// the actual payment, it doesn't create anything. Also reused as-is by
// the account order detail page's payment-method-switch flow (see
// returnContext above) — the Stripe collection UI itself is identical
// either way, only the post-payment destination differs.
export function PaymentStep({ clientSecret, orderNumber, orderId, testMode, providerReference, returnContext = "checkout" }: Props) {
  if (testMode) {
    return (
      <TestPaymentButtons
        orderNumber={orderNumber}
        orderId={orderId}
        providerReference={providerReference ?? ""}
        returnContext={returnContext}
      />
    );
  }
  return (
    <Elements stripe={getStripe()} options={{ clientSecret }}>
      <StripePaymentForm orderNumber={orderNumber} returnContext={returnContext} />
    </Elements>
  );
}

function StripePaymentForm({ orderNumber, returnContext }: { orderNumber: string; returnContext: "checkout" | "account" }) {
  const stripe = useStripe();
  const elements = useElements();
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function handlePay(e: React.FormEvent) {
    e.preventDefault();
    if (!stripe || !elements) return;
    setSubmitting(true);
    setError(null);
    // Redirect-based (PayPal always redirects; cards may need a
    // 3-D-Secure redirect too) — confirmation itself is never trusted
    // client-side, see /checkout/verarbeitung's own comment. `if_required`
    // would skip the redirect for methods that don't need one, but the
    // return_url page's polling handles both cases identically either way,
    // so there's no benefit to branching here.
    const { error: confirmError } = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: `${window.location.origin}/checkout/verarbeitung?orderNumber=${encodeURIComponent(orderNumber)}&context=${returnContext}`,
      },
    });
    // Only reached for immediate client-side failures (e.g. invalid card
    // number) — a redirect on success/pending never returns here at all.
    if (confirmError) {
      setError(confirmError.message ?? "Die Zahlung konnte nicht bestätigt werden.");
      setSubmitting(false);
    }
  }

  return (
    <form onSubmit={handlePay} className="flex flex-col gap-4">
      <PaymentElement />
      {error && <p className="text-sm text-red-600">{error}</p>}
      <button
        type="submit"
        disabled={!stripe || submitting}
        className="rounded-full bg-brand px-6 py-3 text-text-primary font-semibold hover:bg-brand-hover active:scale-[0.97] transition-all disabled:opacity-50"
      >
        {submitting ? "Wird bearbeitet…" : "Jetzt bezahlen"}
      </button>
    </form>
  );
}

function TestPaymentButtons({
  orderNumber,
  orderId,
  providerReference,
  returnContext,
}: {
  orderNumber: string;
  orderId: number;
  providerReference: string;
  returnContext: "checkout" | "account";
}) {
  const router = useRouter();
  const [submitting, setSubmitting] = useState<"paid" | "failed" | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function confirm(paymentStatus: "paid" | "failed") {
    setSubmitting(paymentStatus);
    setError(null);
    try {
      const res = await fetch("/api/webhooks/stripe/test-confirm", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ orderId, providerReference, paymentStatus }),
      });
      const data = await res.json();
      if (!data.ok) {
        setError(data.reason || "Testzahlung fehlgeschlagen.");
        setSubmitting(null);
        return;
      }
      router.push(`/checkout/verarbeitung?orderNumber=${encodeURIComponent(orderNumber)}&context=${returnContext}`);
    } catch {
      setError("Testzahlung konnte nicht ausgeführt werden.");
      setSubmitting(null);
    }
  }

  return (
    <div className="flex flex-col gap-3 rounded-xl border border-dashed border-amber-500 bg-amber-50 p-4">
      <p className="text-sm font-semibold text-amber-800">PAYMENT_TEST_MODE aktiv — kein echtes Stripe-Konto verbunden.</p>
      {error && <p className="text-sm text-red-600">{error}</p>}
      <div className="flex gap-3">
        <button
          type="button"
          onClick={() => confirm("paid")}
          disabled={submitting !== null}
          className="rounded-full bg-green-600 px-5 py-2 text-white font-semibold disabled:opacity-50"
        >
          {submitting === "paid" ? "Wird bestätigt…" : "Testzahlung erfolgreich"}
        </button>
        <button
          type="button"
          onClick={() => confirm("failed")}
          disabled={submitting !== null}
          className="rounded-full bg-red-600 px-5 py-2 text-white font-semibold disabled:opacity-50"
        >
          {submitting === "failed" ? "Wird bestätigt…" : "Testzahlung fehlgeschlagen"}
        </button>
      </div>
    </div>
  );
}
