/**
 * Promo code REST endpoints — wraps Stripe Coupon + Promotion Code APIs.
 *
 * GET    /promos           — list promotion codes
 * POST   /promos           — create coupon + promotion code
 * POST   /promos/validate  — validate a promo code
 * PUT    /promos/:id/deactivate — deactivate a promotion code
 */

import type { FastifyInstance } from 'fastify';
import Stripe from 'stripe';
import { getRequestProductId } from '../../lib/request-context.js';
import { BadRequestError } from '../../lib/errors.js';
import { CreatePromoSchema, type PromoCodeResponse } from './types.js';

let _stripeInstance: Stripe | null = null;

function getStripe(): Stripe {
  if (_stripeInstance) return _stripeInstance;
  const key = process.env.STRIPE_SECRET_KEY;
  if (!key) throw new Error('STRIPE_SECRET_KEY not configured');
  _stripeInstance = new Stripe(key);
  return _stripeInstance;
}

function mapPromo(p: Stripe.PromotionCode): PromoCodeResponse {
  // Stripe API change: the coupon moved under `promotion` (a {type,coupon} union).
  const coupon = p.promotion.type === 'coupon' ? p.promotion.coupon : null;
  const isExpanded = coupon != null && typeof coupon !== 'string';
  return {
    id: p.id,
    code: p.code,
    active: p.active,
    couponId: isExpanded ? coupon.id : ((coupon as string) ?? null),
    percentOff: isExpanded ? coupon.percent_off : null,
    amountOff: isExpanded ? coupon.amount_off : null,
    currency: isExpanded ? coupon.currency : null,
    duration: isExpanded ? coupon.duration : null,
    timesRedeemed: p.times_redeemed,
    maxRedemptions: p.max_redemptions,
    expiresAt: p.expires_at ? new Date(p.expires_at * 1000).toISOString() : null,
    created: new Date(p.created * 1000).toISOString(),
    metadata: (p.metadata ?? {}) as Record<string, string>,
  };
}

export async function promoRoutes(app: FastifyInstance) {
  // List
  app.get('/promos', async req => {
    const { active } = req.query as { active?: string };
    const stripe = getStripe();
    const promos = await stripe.promotionCodes.list({
      limit: 100,
      ...(active !== undefined && { active: active === 'true' }),
    });
    return { promos: promos.data.map(mapPromo) };
  });

  // Create
  app.post('/promos', async (req, reply) => {
    const parsed = CreatePromoSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }
    const input = parsed.data;

    if (!input.percentOff && !input.amountOff) {
      throw new BadRequestError('Either percentOff or amountOff is required');
    }

    const productId = getRequestProductId(req);
    const stripe = getStripe();

    // Create coupon first
    const couponParams: Stripe.CouponCreateParams = {
      duration: input.duration,
      ...(input.percentOff && { percent_off: input.percentOff }),
      ...(input.amountOff && { amount_off: input.amountOff, currency: input.currency }),
      ...(input.duration === 'repeating' &&
        input.durationInMonths && { duration_in_months: input.durationInMonths }),
      metadata: { createdBy: input.createdBy, productId },
    };
    const coupon = await stripe.coupons.create(couponParams);

    // Create promotion code
    const promoParams: Stripe.PromotionCodeCreateParams = {
      promotion: { type: 'coupon', coupon: coupon.id },
      code: input.code.toUpperCase().replace(/[^A-Z0-9-]/g, ''),
      ...(input.maxRedemptions && { max_redemptions: input.maxRedemptions }),
      ...(input.expiresAt && {
        expires_at: Math.floor(new Date(input.expiresAt).getTime() / 1000),
      }),
      metadata: { createdBy: input.createdBy, productId },
    };
    const promo = await stripe.promotionCodes.create(promoParams);

    reply.code(201);
    return mapPromo(promo);
  });

  // Validate
  app.post('/promos/validate', async req => {
    const { code } = req.body as { code?: string };
    if (!code) throw new BadRequestError('code is required');

    const stripe = getStripe();
    const promos = await stripe.promotionCodes.list({
      code,
      active: true,
      limit: 1,
    });

    if (promos.data.length === 0) {
      return { valid: false, promo: null };
    }

    return { valid: true, promo: mapPromo(promos.data[0]) };
  });

  // Deactivate
  app.put('/promos/:id/deactivate', async req => {
    const { id } = req.params as { id: string };
    const stripe = getStripe();

    try {
      const promo = await stripe.promotionCodes.update(id, { active: false });
      return mapPromo(promo);
    } catch {
      throw new BadRequestError('Failed to deactivate promotion code');
    }
  });
}
