import { verifyPassword } from "@pmap/auth";
import { prisma } from "@pmap/database";
import { NextResponse } from "next/server";
import { getAdminSecuritySettings, verifyTotpCode } from "../../../../admin-security";
import { createUserSession, isAdminRole, safeRedirectPath, sessionCookieName, sessionDurationMs } from "../../../../auth";
import { fail } from "../../_lib";

const maxFailedLoginCount = 5;
const lockDurationMs = 1000 * 60 * 15;

export async function POST(request: Request) {
  try {
    const payload = await request.json().catch(() => null) as { email?: unknown; password?: unknown; redirect?: unknown; portal?: unknown; twoFactorCode?: unknown } | null;
    const email = typeof payload?.email === "string" ? payload.email.trim().toLowerCase() : "";
    const password = typeof payload?.password === "string" ? payload.password : "";
    const requestedRedirect = typeof payload?.redirect === "string" ? payload.redirect : undefined;
    const portal = payload?.portal === "admin" ? "admin" : "participant";
    if (!email || !password) return fail(request, "LOGIN_REQUIRED", "Email and password are required.", 422);

    const user = await prisma.user.findUnique({
      where: { email },
      include: { roles: { include: { role: true } } }
    });
    if (!user) return fail(request, "INVALID_LOGIN", "Invalid email or password.", 401);
    if (user.accountStatus !== "ACTIVE") return fail(request, "ACCOUNT_INACTIVE", "This account is not active.", 403);
    if (user.lockedUntil && user.lockedUntil > new Date()) {
      return fail(request, "ACCOUNT_LOCKED", "Too many failed attempts. Please try again later.", 423);
    }

    const isValidPassword = await verifyPassword(password, user.passwordHash);
    if (!isValidPassword) {
      const failedLoginCount = user.failedLoginCount + 1;
      await prisma.user.update({
        where: { id: user.id },
        data: {
          failedLoginCount,
          lockedUntil: failedLoginCount >= maxFailedLoginCount ? new Date(Date.now() + lockDurationMs) : null
        }
      });
      return fail(request, "INVALID_LOGIN", "Invalid email or password.", 401);
    }

    const roles = user.roles.map((userRole) => userRole.role.code);
    const isAdmin = roles.some(isAdminRole);
    const isParticipant = roles.includes("PARTICIPANT");
    if (portal === "admin" && !isAdmin) {
      return fail(request, "ADMIN_ROLE_REQUIRED", "This login is for administrators and staff only.", 403);
    }
    if (portal === "participant" && !isParticipant) {
      return fail(request, "PARTICIPANT_ROLE_REQUIRED", "This login is for participants only.", 403);
    }
    if (portal === "admin") {
      const security = await getAdminSecuritySettings(user.id);
      if (security.twoFactorEnabled && security.twoFactorSecret) {
        const twoFactorCode = typeof payload?.twoFactorCode === "string" ? payload.twoFactorCode : "";
        if (!twoFactorCode) {
          return NextResponse.json({
            ok: true,
            data: {
              requiresTwoFactor: true,
              user: { email: user.email },
              redirectTo: safeRedirectPath(requestedRedirect, roles)
            }
          });
        }
        if (!verifyTotpCode(security.twoFactorSecret, twoFactorCode)) {
          return fail(request, "TWO_FACTOR_INVALID", "The authenticator code is invalid or expired.", 401);
        }
      }
    }

    const session = await createUserSession(user.id, request);
    await prisma.user.update({
      where: { id: user.id },
      data: { failedLoginCount: 0, lockedUntil: null }
    });

    const response = NextResponse.json({
      ok: true,
      data: {
        user: { id: user.id, email: user.email, roles },
        redirectTo: safeRedirectPath(requestedRedirect, roles)
      }
    });
    response.cookies.set(sessionCookieName, session.token, {
      httpOnly: true,
      sameSite: "lax",
      secure: process.env.NODE_ENV === "production",
      path: "/",
      expires: session.expiresAt,
      maxAge: Math.floor(sessionDurationMs / 1000)
    });
    return response;
  } catch (error) {
    return fail(request, "LOGIN_FAILED", "Login could not be completed.", 500, error instanceof Error ? error.message : error);
  }
}
