import { randomBytes } from 'node:crypto';
import type { FastifyInstance } from 'fastify';
import { extractAuth } from '../../lib/auth.js';
import { BadRequestError, NotFoundError } from '../../lib/errors.js';
import { CreateWebhookSubscriptionSchema, UpdateWebhookSubscriptionSchema } from './types.js';
import * as repo from './repository.js';
import { dispatchEvent } from './dispatcher.js';

const DEFAULT_PRODUCT_ID = 'lysnrai';

export async function webhookRoutes(app: FastifyInstance) {
  // List webhook subscriptions
  app.get('/webhooks/subscriptions', async req => {
    await extractAuth(req);
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;
    return repo.listSubscriptions(productId);
  });

  // Create a webhook subscription
  app.post('/webhooks/subscriptions', async req => {
    const auth = await extractAuth(req);
    const parsed = CreateWebhookSubscriptionSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const secret = randomBytes(32).toString('hex');
    const sub = await repo.createSubscription(parsed.data, secret, auth.sub);

    // Return secret only on creation (shown once)
    return { ...sub, secret };
  });

  // Get a specific subscription
  app.get('/webhooks/subscriptions/:id', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;

    const sub = await repo.getSubscription(id, productId);
    if (!sub) throw new NotFoundError('Webhook subscription not found');

    // Redact secret (only shown on creation)
    return { ...sub, secret: `${sub.secret.slice(0, 8)}...` };
  });

  // Update a subscription
  app.patch('/webhooks/subscriptions/:id', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;

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

    const updated = await repo.updateSubscription(id, productId, parsed.data);
    if (!updated) throw new NotFoundError('Webhook subscription not found');
    return { ...updated, secret: `${updated.secret.slice(0, 8)}...` };
  });

  // Delete a subscription
  app.delete('/webhooks/subscriptions/:id', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;

    const deleted = await repo.deleteSubscription(id, productId);
    if (!deleted) throw new NotFoundError('Webhook subscription not found');
    return { success: true };
  });

  // List deliveries for a subscription
  app.get('/webhooks/subscriptions/:id/deliveries', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    return repo.listDeliveries(id, {
      limit: query.limit ? parseInt(query.limit, 10) : undefined,
    });
  });

  // Test delivery — send a test event to a specific subscription
  app.post('/webhooks/subscriptions/:id/test', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;

    const sub = await repo.getSubscription(id, productId);
    if (!sub) throw new NotFoundError('Webhook subscription not found');

    await dispatchEvent(
      productId,
      'user.created',
      { test: true, message: 'This is a test webhook delivery' },
      req.log
    );

    return { success: true, message: 'Test event dispatched' };
  });

  // Rotate subscription secret
  app.post('/webhooks/subscriptions/:id/rotate-secret', async req => {
    await extractAuth(req);
    const { id } = req.params as { id: string };
    const query = req.query as Record<string, string>;
    const productId = query.productId || DEFAULT_PRODUCT_ID;

    const sub = await repo.getSubscription(id, productId);
    if (!sub) throw new NotFoundError('Webhook subscription not found');

    const newSecret = randomBytes(32).toString('hex');
    sub.secret = newSecret;
    sub.updatedAt = new Date().toISOString();

    // upsert the full doc to persist the new secret (not exposed via UpdateSchema)
    const { getCollection } = await import('../../lib/datastore.js');
    await getCollection('webhook_subscriptions', '/productId').upsert(sub);

    return { secret: newSecret, message: 'Secret rotated. Update your webhook consumer.' };
  });
}
