/**
 * Referral REST endpoints.
 *
 * GET    /referrals              — list all referrals
 * GET    /referrals/stats        — count stats (total, completed, rewarded)
 * GET    /referrals/by-referrer/:referrerId — list by referrer
 * GET    /referrals/by-email/:email         — get by referred email
 * POST   /referrals              — create a referral
 * PUT    /referrals/:id          — update status
 */

import type { FastifyInstance } from 'fastify';
import { getRequestProductId } from '../../lib/request-context.js';
import { BadRequestError, NotFoundError } from '../../lib/errors.js';
import { dispatchReferralStatusChanged } from '../../lib/webhooks.js';
import * as repo from './repository.js';
import { CreateReferralSchema, UpdateReferralStatusSchema, type ReferralDoc } from './types.js';

export async function referralRoutes(app: FastifyInstance) {
  // List all
  app.get('/referrals', async req => {
    const { limit = '100', offset = '0' } = req.query as Record<string, string>;
    const productId = getRequestProductId(req);
    const items = await repo.listAll(Number(limit), Number(offset), productId);
    return { referrals: items, count: items.length };
  });

  // Stats
  app.get('/referrals/stats', async req => {
    const productId = getRequestProductId(req);
    return repo.countReferrals(productId);
  });

  // By referrer
  app.get('/referrals/by-referrer/:referrerId', async req => {
    const { referrerId } = req.params as { referrerId: string };
    const productId = getRequestProductId(req);
    const items = await repo.getByReferrer(referrerId, productId);
    return { referrals: items, count: items.length };
  });

  // By referred email
  app.get('/referrals/by-email/:email', async req => {
    const { email } = req.params as { email: string };
    const productId = getRequestProductId(req);
    const doc = await repo.getByReferredEmail(email, productId);
    if (!doc) throw new NotFoundError('Referral not found');
    return doc;
  });

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

    // Check if referral already exists for this email
    const productId = getRequestProductId(req);
    const existing = await repo.getByReferredEmail(input.referredEmail, productId);
    if (existing) {
      throw new BadRequestError('A referral already exists for this email');
    }

    const now = new Date().toISOString();
    const doc: ReferralDoc = {
      id: `ref_${crypto.randomUUID()}`,
      productId,
      referrerId: input.referrerId,
      referrerEmail: input.referrerEmail,
      referredUserId: null,
      referredEmail: input.referredEmail.toLowerCase(),
      status: 'pending',
      referrerRewardTokens: input.referrerRewardTokens,
      referredRewardTokens: input.referredRewardTokens,
      referrerRewarded: false,
      referredRewarded: false,
      createdAt: now,
      completedAt: null,
    };
    const created = await repo.create(doc);
    reply.code(201);
    return created;
  });

  // Update status
  app.put('/referrals/:id', async req => {
    const { id } = req.params as { id: string };
    const { referrerId } = req.query as { referrerId?: string };
    if (!referrerId) {
      throw new BadRequestError('referrerId query parameter is required');
    }

    const parsed = UpdateReferralStatusSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const updates: Partial<ReferralDoc> = { ...parsed.data };
    if (
      parsed.data.status === 'rewarded' ||
      parsed.data.status === 'subscribed' ||
      parsed.data.status === 'signed_up'
    ) {
      updates.completedAt = updates.completedAt ?? new Date().toISOString();
    }

    const updated = await repo.update(id, referrerId, updates);
    if (!updated) throw new NotFoundError('Referral not found');

    // Fire-and-forget webhook callback on status transition
    dispatchReferralStatusChanged({
      referralId: updated.id,
      referrerId: updated.referrerId,
      referredEmail: updated.referredEmail,
      previousStatus: parsed.data.status, // requested status
      newStatus: updated.status,
      referrerRewarded: updated.referrerRewarded,
      referredRewarded: updated.referredRewarded,
    });

    return updated;
  });
}
