import { NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@server/db/drizzle';
import { sql } from 'drizzle-orm';
import { initDatabase } from '@server/db/init';
import { withErrorHandler } from '@server/middleware/withErrorHandler';
import { withAuth, AuthedRequest } from '@server/middleware/withAuth';
import { issueOtp } from '@server/services/email/otp.service';
import { ValidationError, ForbiddenError } from '@server/errors';

const schema = z.object({
  newEmail: z.string().email('Invalid email address'),
});

export const POST = withErrorHandler(
  withAuth(async (req: AuthedRequest) => {
    await initDatabase();
    const { userId, role } = req.session;

    const ALLOWED_ROLES = new Set(['owner', 'manager', 'admin']);
    if (!ALLOWED_ROLES.has(role)) {
      throw new ForbiddenError();
    }

    let body: unknown;
    try { body = await req.json(); } catch { throw new ValidationError('Request body must be valid JSON'); }

    const parsed = schema.safeParse(body);
    if (!parsed.success) {
      throw new ValidationError(parsed.error.issues[0]?.message ?? 'Invalid request');
    }
    const { newEmail } = parsed.data;
    const normalized = newEmail.toLowerCase().trim();

    const { rows: existing } = await db.execute(sql`
      SELECT 1 FROM users WHERE lower(email) = ${normalized} LIMIT 1
    `);
    if ((existing as unknown[]).length > 0) {
      throw new ValidationError('This email is already in use');
    }

    await db.execute(sql`
      UPDATE users SET pending_email = ${normalized} WHERE id = ${userId}
    `);

    const { alreadySentRecently } = await issueOtp(normalized, 'email_change');

    return NextResponse.json({ ok: true, alreadySentRecently });
  })
);
