/**
 * Magic link endpoints for SmartAuth Phase 6B.
 *
 * POST /auth/magic-link        — send magic link email (15 min TTL)
 * POST /auth/magic-link/verify — verify token, issue JWT
 */

import type { FastifyInstance } from 'fastify';
import { BadRequestError, UnauthorizedError } from '../../../lib/errors.js';
import { bus } from '../../../lib/event-bus.js';
import * as repo from './repository.js';
import * as userRepo from '../repository.js';
import * as jwt from '../jwt.js';
import { MagicLinkRequestSchema, MagicLinkVerifySchema } from './types.js';
import type { MagicLinkTokenDoc } from './types.js';
import { randomUUID } from 'node:crypto';

const MAGIC_LINK_TTL_MS = 15 * 60 * 1000; // 15 minutes

export async function magicLinkRoutes(app: FastifyInstance) {
  // ── Send magic link ────────────────────────────────────────

  app.post('/auth/magic-link', async req => {
    const parsed = MagicLinkRequestSchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const { email, productId } = parsed.data;

    // Anti-enumeration: always return success regardless of whether user exists
    const user = await userRepo.getByEmail(email, productId);

    if (user) {
      const token = repo.generateToken();
      const tokenHash = repo.hashToken(token);
      const now = new Date();

      const doc: MagicLinkTokenDoc = {
        id: `ml_${randomUUID()}`,
        productId,
        email: email.toLowerCase(),
        tokenHash,
        expiresAt: new Date(now.getTime() + MAGIC_LINK_TTL_MS).toISOString(),
        usedAt: null,
        createdAt: now.toISOString(),
      };

      await repo.create(doc);

      // Emit event for delivery module to send email
      await bus.emit('auth.magic_link_requested', {
        userId: user.id,
        email: user.email,
        token,
        productId,
        expiresAt: doc.expiresAt,
      });

      req.log.info({ email: user.email, productId }, '[auth] Magic link sent');
    } else {
      req.log.info(
        { email, productId },
        '[auth] Magic link requested for unknown email (anti-enumeration)'
      );
    }

    // Always return success
    return { message: 'If the email exists, a magic link has been sent' };
  });

  // ── Verify magic link ──────────────────────────────────────

  app.post('/auth/magic-link/verify', async req => {
    const parsed = MagicLinkVerifySchema.safeParse(req.body);
    if (!parsed.success) {
      throw new BadRequestError(parsed.error.issues.map(i => i.message).join('; '));
    }

    const { token, email } = parsed.data;
    const tokenHash = repo.hashToken(token);

    // Find the user first to get productId
    const user = await userRepo.getByEmailCrossProduct(email);
    if (!user) {
      throw new UnauthorizedError('Invalid or expired magic link');
    }

    const doc = await repo.findByHash(tokenHash, user.productId);
    if (!doc) {
      throw new UnauthorizedError('Invalid or expired magic link');
    }

    // Check expiry
    if (new Date(doc.expiresAt).getTime() < Date.now()) {
      throw new UnauthorizedError('Magic link has expired');
    }

    // Check already used
    if (doc.usedAt) {
      throw new UnauthorizedError('Magic link has already been used');
    }

    // Check email match
    if (doc.email.toLowerCase() !== email.toLowerCase()) {
      throw new UnauthorizedError('Invalid or expired magic link');
    }

    // Mark as used
    await repo.markUsed(doc.id, doc.productId);

    // Mark email as verified if not already
    if (!user.emailVerified) {
      await userRepo.update(user.id, { emailVerified: true });
    }

    // Issue tokens
    const accessToken = await jwt.createAccessToken({
      sub: user.id,
      email: user.email,
      role: user.role as string,
      productId: user.productId,
      plan: (user.plan ?? 'free') as 'free' | 'pro' | 'enterprise',
      displayName: user.displayName,
    });
    const refreshToken = await jwt.createRefreshToken({
      sub: user.id,
      productId: user.productId,
    });

    req.log.info({ userId: user.id, email: user.email }, '[auth] Magic link login successful');
    return {
      accessToken,
      refreshToken,
      user: { id: user.id, email: user.email, displayName: user.displayName },
    };
  });
}
