import { hashPassword, verifyPassword } from "@pmap/auth";
import { prisma } from "@pmap/database";
import QRCode from "qrcode";
import { getAdminSessionFromRequest, hashSessionToken, sessionCookieName } from "../../../../../auth";
import { authenticatorSetupUrl, generateTotpSecret, getAdminSecuritySettings, saveAdminSecuritySettings, type AdminSecuritySettings } from "../../../../../admin-security";
import { fail, ok } from "../../../_lib";

function currentToken(request: Request) {
  const token = request.headers.get("cookie")?.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${sessionCookieName}=`))?.split("=")[1];
  return token ? decodeURIComponent(token) : undefined;
}

async function securityResponse(email: string, security: AdminSecuritySettings) {
  const setupUrl = security.twoFactorSecret ? authenticatorSetupUrl(email, security.twoFactorSecret) : null;
  return {
    ...security,
    setupUrl,
    qrCodeDataUrl: setupUrl ? await QRCode.toDataURL(setupUrl, { margin: 1, width: 220 }) : null
  };
}

export async function GET(request: Request) {
  try {
    const session = await getAdminSessionFromRequest(request);
    if (!session) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    return ok(request, {
      user: {
        email: session.email,
        roles: session.roles
      },
      security: await securityResponse(session.email, await getAdminSecuritySettings(session.id))
    });
  } catch (error) {
    return fail(request, "PROFILE_SECURITY_UNAVAILABLE", "Profile security settings could not be loaded.", 500, error instanceof Error ? error.message : error);
  }
}

export async function PATCH(request: Request) {
  try {
    const session = await getAdminSessionFromRequest(request);
    if (!session) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    const payload = await request.json().catch(() => null) as { twoFactorEnabled?: unknown; authenticatorConfigured?: unknown } | null;
    const current = await getAdminSecuritySettings(session.id);
    const twoFactorEnabled = payload?.twoFactorEnabled === true;
    const shouldPrepareAuthenticator = payload?.authenticatorConfigured === true || twoFactorEnabled;
    const twoFactorSecret = shouldPrepareAuthenticator ? current.twoFactorSecret ?? generateTotpSecret() : current.twoFactorSecret;
    const next = {
      ...current,
      twoFactorEnabled,
      authenticatorConfigured: shouldPrepareAuthenticator || current.authenticatorConfigured,
      recoveryCodesGeneratedAt: shouldPrepareAuthenticator && !current.recoveryCodesGeneratedAt ? new Date().toISOString() : current.recoveryCodesGeneratedAt,
      twoFactorSecret
    };
    const saved = await saveAdminSecuritySettings(session.id, next);
    return ok(request, { security: await securityResponse(session.email, saved) });
  } catch (error) {
    return fail(request, "PROFILE_SECURITY_UPDATE_FAILED", "Profile security settings could not be saved.", 500, error instanceof Error ? error.message : error);
  }
}

export async function POST(request: Request) {
  try {
    const session = await getAdminSessionFromRequest(request);
    if (!session) return fail(request, "UNAUTHORIZED", "Admin login is required.", 401);
    const payload = await request.json().catch(() => null) as { currentPassword?: unknown; newPassword?: unknown; confirmPassword?: unknown } | null;
    const currentPassword = typeof payload?.currentPassword === "string" ? payload.currentPassword : "";
    const newPassword = typeof payload?.newPassword === "string" ? payload.newPassword : "";
    const confirmPassword = typeof payload?.confirmPassword === "string" ? payload.confirmPassword : "";
    if (newPassword.length < 12) return fail(request, "PASSWORD_TOO_SHORT", "New password must be at least 12 characters.", 422);
    if (newPassword !== confirmPassword) return fail(request, "PASSWORD_MISMATCH", "New password and confirmation do not match.", 422);

    const user = await prisma.user.findUnique({ where: { id: session.id } });
    if (!user) return fail(request, "USER_NOT_FOUND", "Signed-in user could not be found.", 404);
    if (!await verifyPassword(currentPassword, user.passwordHash)) return fail(request, "CURRENT_PASSWORD_INVALID", "Current password is incorrect.", 401);

    const token = currentToken(request);
    const keepTokenHash = token ? hashSessionToken(token) : "";
    await prisma.$transaction([
      prisma.user.update({
        where: { id: session.id },
        data: { passwordHash: await hashPassword(newPassword), failedLoginCount: 0, lockedUntil: null }
      }),
      prisma.userSession.updateMany({
        where: {
          userId: session.id,
          revokedAt: null,
          tokenHash: { not: keepTokenHash }
        },
        data: { revokedAt: new Date() }
      })
    ]);

    return ok(request, { passwordChanged: true, otherSessionsRevoked: true });
  } catch (error) {
    return fail(request, "PASSWORD_CHANGE_FAILED", "Password could not be changed.", 500, error instanceof Error ? error.message : error);
  }
}
