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 { verifyOtp } from '@server/services/email/otp.service';
import { ValidationError, AuthError, ForbiddenError } from '@server/errors';
import { createSession, COOKIE_NAME } from '@server/auth';

const schema = z.object({
  code: z.string().regex(/^\d{6}$/, 'Code must be exactly 6 digits'),
});

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 { code } = parsed.data;

    const { rows } = await db.execute(sql`
      SELECT pending_email FROM users WHERE id = ${userId} LIMIT 1
    `);
    const row = (rows as Array<{ pending_email: string | null }>)[0];
    if (!row?.pending_email) {
      throw new ValidationError('No pending email change found. Please start the process again.');
    }
    const pendingEmail = row.pending_email;

    const valid = await verifyOtp(pendingEmail, code, 'email_change');
    if (!valid) {
      throw new AuthError('Invalid or expired code');
    }

    try {
      await db.execute(sql`
        UPDATE users SET email = ${pendingEmail}, pending_email = NULL WHERE id = ${userId}
      `);
    } catch (err: unknown) {
      const msg = String((err as { message?: string })?.message ?? '');
      if (msg.includes('unique') || msg.includes('duplicate') || msg.includes('23505')) {
        throw new ValidationError('This email is already in use. Please request a new code with a different address.');
      }
      throw err;
    }

    const newToken = await createSession({ ...req.session, email: pendingEmail });
    const response = NextResponse.json({ ok: true, email: pendingEmail });
    response.cookies.set(COOKIE_NAME, newToken, {
      httpOnly: true,
      secure: process.env.NODE_ENV === 'production',
      sameSite: 'lax',
      maxAge: 7 * 24 * 60 * 60,
      path: '/',
    });
    return response;
  })
);
