import Stripe from "stripe";
import type { PaymentProvider, CreatePaymentIntentInput, CreatePaymentIntentResult } from "./types";

// Server-only — never imported from a "use client" file. Same
// process.env-at-point-of-use convention as vies.ts/brevo.ts (no
// throwing on a missing key; an unset STRIPE_SECRET_KEY just makes every
// call fail at request time, which is the expected state whenever
// PAYMENT_TEST_MODE is on and this module is never actually invoked).
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY || "";

let client: Stripe | null = null;
function getClient(): Stripe {
  if (!client) client = new Stripe(STRIPE_SECRET_KEY);
  return client;
}

async function createPaymentIntent(input: CreatePaymentIntentInput): Promise<CreatePaymentIntentResult> {
  // automatic_payment_methods lets Stripe itself decide card vs. PayPal
  // vs. any other method active on this account/region — one PaymentIntent
  // covers both required methods, per the plan's provider choice (Payment
  // Element, not per-method Checkout Sessions).
  const intent = await getClient().paymentIntents.create({
    amount: input.amountCents,
    currency: input.currency,
    receipt_email: input.customerEmail,
    description: input.description,
    automatic_payment_methods: { enabled: true },
  });
  if (!intent.client_secret) throw new Error("Stripe did not return a client_secret");
  return { clientSecret: intent.client_secret, providerReference: intent.id };
}

// Called right after the order is persisted in Payload (see
// app/api/checkout/route.ts) — the PaymentIntent has to exist before the
// order can reference its id (providerReference), so metadata pointing
// the other way (PaymentIntent -> order) can only be attached in a
// second call, not at creation. This is what lets
// app/api/webhooks/stripe/route.ts resolve an incoming
// `payment_intent.*` event back to a specific Payload order without a
// separate, unauthenticated-from-Stripe's-side lookup endpoint.
//
// Awaited but non-fatal to checkout on failure (see the call site) — the
// order and its own `providerReference` field are already the source of
// truth for admin/cleanup-job reconciliation; this metadata only matters
// for the webhook's fast path.
async function attachOrderMetadata(providerReference: string, metadata: { orderId: string; orderNumber: string }): Promise<void> {
  await getClient().paymentIntents.update(providerReference, { metadata });
}

export const stripeProvider: PaymentProvider = { createPaymentIntent, attachOrderMetadata };

const PAYMENT_METHOD_LABELS: Record<string, string> = { card: "Kreditkarte", paypal: "PayPal" };

// Called only by the real webhook route on `payment_intent.succeeded` —
// the checkout route snapshots a neutral "Online-Zahlung" title at order
// creation (see its own comment: the customer hasn't chosen an instrument
// yet at that point, Stripe's Payment Element does that next), this
// resolves the actual one once Stripe reports it so the order/invoice/
// confirmation email reflect what was really used, not a placeholder.
// Best-effort: an unresolvable label just leaves the neutral title in
// place (confirmPayment.ts only overwrites paymentMethodTitle when this
// returns something), it doesn't fail the payment confirmation itself.
export async function resolveStripePaymentMethodLabel(intent: Stripe.PaymentIntent): Promise<string | undefined> {
  const pm = intent.payment_method;
  const pmId = typeof pm === "string" ? pm : pm?.id;
  if (!pmId) return undefined;
  try {
    const resolved = pm && typeof pm === "object" ? pm : await getClient().paymentMethods.retrieve(pmId);
    return PAYMENT_METHOD_LABELS[resolved.type] ?? resolved.type;
  } catch {
    return undefined;
  }
}

// Only used by the real webhook route (never through the PaymentProvider
// interface — signature verification is inherently Stripe-shaped, no
// other provider exists to share this contract with yet).
export function verifyStripeWebhookSignature(rawBody: string, signatureHeader: string): Stripe.Event | null {
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET || "";
  try {
    return getClient().webhooks.constructEvent(rawBody, signatureHeader, webhookSecret);
  } catch {
    return null;
  }
}
