import { NextResponse } from 'next/server';
import bcrypt from 'bcryptjs';
import { eq, and, gt } from 'drizzle-orm';
import { db, users, passwordResetTokens } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withValidation, ParsedRequest } from '@server/middleware/withValidation';
import { resetPasswordSchema, ResetPasswordInput } from '@server/validators/auth.validator';
import { ValidationError } from '@server/errors';
import { withRateLimit, getClientIp, checkRateLimit } from '@server/middleware/withRateLimit';

export const POST = withErrorHandler(
  withRateLimit(
    [
      { scope: 'auth:reset:ip-hour', limit: 10, windowMs: 60 * 60_000, keyOf: (req) => getClientIp(req) },
      { scope: 'auth:reset:ip-day', limit: 50, windowMs: 24 * 60 * 60_000, keyOf: (req) => getClientIp(req) },
    ],
    withValidation(resetPasswordSchema, async (req: ParsedRequest<ResetPasswordInput>) => {
      await initDatabase();
      const { token, newPassword } = req.parsedBody;

      // Per-token rate limit (prevents brute-forcing a single reset token)
      const tokenLimit = checkRateLimit('auth:reset:token-hour', token, 10, 60 * 60_000);
      if (!tokenLimit.allowed) {
        return NextResponse.json(
          { error: 'Too many attempts on this reset token. Please request a new one.', code: 'RATE_LIMITED', retryAfter: tokenLimit.retryAfterSec },
          { status: 429, headers: { 'Retry-After': String(tokenLimit.retryAfterSec ?? 3600) } }
        );
      }

      const rows = await db
        .select({ userId: passwordResetTokens.userId, expiresAt: passwordResetTokens.expiresAt })
        .from(passwordResetTokens)
        .where(
          and(
            eq(passwordResetTokens.token, token),
            eq(passwordResetTokens.used, false),
            gt(passwordResetTokens.expiresAt, sql`NOW()`)
          )
        );
      const resetRecord = rows[0];

      if (!resetRecord) throw new ValidationError('Invalid or expired reset token. Please request a new one.');

      const passwordHash = await bcrypt.hash(newPassword, 12);

      await db.update(users).set({ passwordHash }).where(eq(users.id, resetRecord.userId));
      await db.update(passwordResetTokens).set({ used: true }).where(eq(passwordResetTokens.token, token));

      return NextResponse.json({ success: true });
    })
  )
);
