import { createHash } from 'crypto';
import Stripe from 'stripe';

let _stripe = null;

export function getStripe() {
  const secretKey = process.env.STRIPE_SECRET_KEY;

  if (!secretKey) {
    throw new Error('Stripe is not configured. Please set STRIPE_SECRET_KEY environment variable.');
  }

  if (!_stripe) {
    _stripe = new Stripe(secretKey, { apiVersion: '2023-10-16' });
  }

  return _stripe;
}

export function isEnabled() {
  return !!(process.env.STRIPE_SECRET_KEY && process.env.STRIPE_PUBLISHABLE_KEY);
}

export function getPublishableKey() {
  return process.env.STRIPE_PUBLISHABLE_KEY || null;
}

export async function createCheckoutSession(options) {
  const stripe = getStripe();

  const {
    lineItems,
    successUrl,
    cancelUrl,
    customerEmail,
    metadata = {},
    mode = 'payment',
    clientReferenceId,
  } = options;

  const sessionConfig = {
    line_items: lineItems,
    mode,
    success_url: successUrl,
    cancel_url: cancelUrl,
    metadata,
  };

  if (customerEmail) sessionConfig.customer_email = customerEmail;
  if (clientReferenceId) sessionConfig.client_reference_id = clientReferenceId;

  return stripe.checkout.sessions.create(sessionConfig);
}

export async function createPaymentIntent(options) {
  const stripe = getStripe();

  const {
    amount,
    currency = process.env.ZEN_CURRENCY?.toLowerCase() || 'cad',
    metadata = {},
    automaticPaymentMethods = { enabled: true },
  } = options;

  return stripe.paymentIntents.create({
    amount,
    currency,
    metadata,
    automatic_payment_methods: automaticPaymentMethods,
  });
}

export async function getCheckoutSession(sessionId) {
  return getStripe().checkout.sessions.retrieve(sessionId);
}

export async function getPaymentIntent(paymentIntentId) {
  return getStripe().paymentIntents.retrieve(paymentIntentId);
}

export async function verifyWebhookSignature(payload, signature) {
  const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;

  if (!webhookSecret) {
    throw new Error('Stripe webhook secret is not configured');
  }

  return getStripe().webhooks.constructEvent(payload, signature, webhookSecret);
}

export async function createCustomer(options) {
  const { email, name, metadata = {} } = options;
  return getStripe().customers.create({ email, name, metadata });
}

/**
 * Get or create a customer by email.
 *
 * TOCTOU note: Stripe does not provide a native atomic upsert. To minimise
 * the race-condition window where two concurrent calls both create a customer
 * for the same email, we use an idempotency key derived from the email address.
 * If a duplicate is created despite this guard (extremely unlikely), operators
 * should merge duplicates via the Stripe dashboard or a reconciliation job.
 */
export async function getOrCreateCustomer(email, defaultData = {}) {
  const stripe = getStripe();

  const existing = await stripe.customers.list({ email, limit: 1 });
  if (existing.data.length > 0) return existing.data[0];

  const idempotencyKey = 'get-or-create-customer-' + createHash('sha256').update(email).digest('hex');
  return stripe.customers.create({ email, ...defaultData }, { idempotencyKey });
}

export async function listPaymentMethods(customerId, type = 'card') {
  const methods = await getStripe().paymentMethods.list({ customer: customerId, type });
  return methods.data;
}

export async function createRefund(options) {
  const { paymentIntentId, amount, reason } = options;

  const refundConfig = { payment_intent: paymentIntentId };
  if (amount) refundConfig.amount = amount;
  if (reason) refundConfig.reason = reason;

  return getStripe().refunds.create(refundConfig);
}
