import { NextResponse } from 'next/server';
import crypto from 'crypto';
import { eq, and } from 'drizzle-orm';
import { db, users, passwordResetTokens } from '@server/db/drizzle';
import { initDatabase } from '@server/db/init';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withValidation, ParsedRequest } from '@server/middleware/withValidation';
import { forgotPasswordSchema, ForgotPasswordInput } from '@server/validators/auth.validator';
import { enqueueEmail } from '@server/services/email/outbox.service';
import { withRateLimit, getClientIp, checkRateLimit } from '@server/middleware/withRateLimit';

import { childLogger } from '@server/logger';
const log = childLogger('route.auth.forgot_password');

export const POST = withErrorHandler(
  withRateLimit(
    [
      { scope: 'auth:forgot:ip-hour', limit: 5, windowMs: 60 * 60_000, keyOf: (req) => getClientIp(req) },
      { scope: 'auth:forgot:ip-day', limit: 20, windowMs: 24 * 60 * 60_000, keyOf: (req) => getClientIp(req) },
    ],
    withValidation(forgotPasswordSchema, async (req: ParsedRequest<ForgotPasswordInput>) => {
      await initDatabase();
      const { email } = req.parsedBody;
      const normalizedEmail = email.toLowerCase().trim();

      // Per-email rate limit
      const emailLimit = checkRateLimit('auth:forgot:email-hour', normalizedEmail, 3, 60 * 60_000);
      if (!emailLimit.allowed) {
        return NextResponse.json(
          { error: 'Too many password reset requests. Please try again later.', code: 'RATE_LIMITED', retryAfter: emailLimit.retryAfterSec },
          { status: 429, headers: { 'Retry-After': String(emailLimit.retryAfterSec ?? 3600) } }
        );
      }

      const rows = await db
        .select({ id: users.id, name: users.name, email: users.email })
        .from(users)
        .where(and(eq(users.email, normalizedEmail), eq(users.isActive, true)));
      const user = rows[0];

      if (!user) {
        return NextResponse.json({
          success: true,
          message: 'If an account exists with that email, you can reset your password.',
        });
      }

      const token = crypto.randomBytes(32).toString('hex');
      const expiresAt = new Date(Date.now() + 30 * 60 * 1000).toISOString();

      await db
        .insert(passwordResetTokens)
        .values({ userId: user.id, token, expiresAt })
        .onConflictDoUpdate({
          target: passwordResetTokens.userId,
          set: { token, expiresAt, used: false },
        });

      const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || '';
      const resetLink = `${baseUrl}/reset-password?token=${token}`;

      try {
        await enqueueEmail({
          to: user.email,
          templateKey: 'password_reset',
          channel: 'immediate',
          kind: 'password_reset',
          vars: { name: user.name || user.email, resetLink },
        });
      } catch (emailErr) {
        log.error({ err: emailErr }, 'ForgotPassword email enqueue error');
      }

      const response: Record<string, string | boolean> = {
        success: true,
        message: 'If an account exists with that email, a password reset has been initiated.',
      };

      if (process.env.NODE_ENV !== 'production') {
        response.resetLink = resetLink;
        response.devNote = 'Reset link shown only in development mode. In production, send this via email.';
      }

      return NextResponse.json(response);
    })
  )
);
