import { NextRequest, NextResponse } from "next/server";

import {
  SESSION_COOKIE,
  SESSION_COOKIE_SECURE_ENV,
  getSessionCookieDomain,
  getSessionTtlSeconds,
  UserRole,
  canAccessPath,
  authenticateUser,
  getDefaultPathForRole,
  serializeSession
} from "@/lib/auth";
import { getRequestAuditContext, writeAuditTrail } from "@/lib/audit";
import { consumeRateLimit, getRateLimitHeaders } from "@/lib/rate-limit";
import { prisma } from "@/lib/prisma";
import { getRequestBaseUrl } from "@/lib/request-url";

function getSafePath(value: string | null) {
  if (!value) {
    return null;
  }

  if (!value.startsWith("/")) {
    return null;
  }

  if (value.startsWith("//")) {
    return null;
  }

  return value;
}

function resolveNumber(raw: string | undefined, fallback: number) {
  const value = Number(raw?.trim());
  if (!Number.isInteger(value) || value <= 0) {
    return fallback;
  }

  return value;
}

const AUTH_DEBUG = process.env.AUTH_DEBUG === "true" || process.env.AUTH_DEBUG === "1";

function maskEmail(email: string) {
  if (!email) {
    return "";
  }

  const [name, domain] = email.split("@");
  if (!domain) {
    return "*****";
  }

  if (name.length <= 2) {
    return `${name[0]}***@${domain}`;
  }

  return `${name.slice(0, 2)}***@${domain}`;
}

function shouldUseSecureCookies(request: NextRequest) {
  const explicit = SESSION_COOKIE_SECURE_ENV;
  if (explicit === "true" || explicit === "1") {
    return true;
  }

  if (explicit === "false" || explicit === "0") {
    return false;
  }

  const forwardedProto = request.headers.get("x-forwarded-proto");
  return request.nextUrl.protocol === "https:" || (forwardedProto?.split(",")[0]?.toLowerCase() === "https");
}

export async function POST(request: NextRequest) {
  const { ipAddress, userAgent } = await getRequestAuditContext();
  const baseUrl = getRequestBaseUrl(request);
  const retryControl = consumeRateLimit(ipAddress || "unknown", {
    scope: "auth_login",
    limit: resolveNumber(process.env.LOGIN_RATE_LIMIT_ATTEMPTS, 10),
    windowMs: resolveNumber(process.env.LOGIN_RATE_LIMIT_WINDOW_MS, 15 * 60 * 1000)
  });

  if (!retryControl.allowed) {
    const loginUrl = new URL("/login", baseUrl);
    loginUrl.searchParams.set("error", "rate_limited");
    const response = NextResponse.redirect(loginUrl);
    const headers = getRateLimitHeaders(retryControl);
    Object.entries(headers).forEach(([headerName, headerValue]) => {
      response.headers.set(headerName, headerValue);
    });
    return response;
  }

  const form = await request.formData();
  const rawEmail = form.get("email");
  const rawPassword = form.get("password");
  const rawNext = form.get("next");

  const next = getSafePath(typeof rawNext === "string" ? rawNext : null);
  const email = typeof rawEmail === "string" ? rawEmail.trim() : "";
  const password = typeof rawPassword === "string" ? rawPassword : "";
  if (AUTH_DEBUG) {
    console.warn("[AUTH] login_attempt", {
      email: maskEmail(email),
      hasPassword: password.length > 0,
      next,
      ipAddress,
      userAgent
    });
  }

  if (!email || !password) {
    const loginUrl = new URL("/login", baseUrl);
    loginUrl.searchParams.set("error", "credentials_required");
    if (next) {
      loginUrl.searchParams.set("next", next);
    }

    return NextResponse.redirect(loginUrl);
  }

  const session = await authenticateUser(email, password);
  if (!session) {
    const attemptedUser = await prisma.user.findUnique({
      where: { email },
      select: { id: true, tenantId: true, status: true }
    });

    if (attemptedUser) {
      await writeAuditTrail({
        tenantId: attemptedUser.tenantId,
        actorUserId: attemptedUser.id,
        entityType: "user",
        entityId: attemptedUser.id,
        action: "user_login_failed",
        metadata: {
          email,
          status: attemptedUser.status,
          source: "web"
        },
        ipAddress,
        userAgent
      });
    }

    const loginUrl = new URL("/login", baseUrl);
    loginUrl.searchParams.set("error", "invalid_credentials");
    if (next) {
      loginUrl.searchParams.set("next", next);
    }

    if (AUTH_DEBUG) {
      console.warn("[AUTH] login_failed", {
        email: maskEmail(email),
        ipAddress,
        userAgent
      });
    }

    return NextResponse.redirect(loginUrl);
  }

  await prisma.user.update({
    where: { id: session.userId },
    data: { lastLoginAt: new Date() }
  });

  await writeAuditTrail({
    tenantId: session.tenantId,
    actorUserId: session.userId,
    entityType: "user",
    entityId: session.userId,
    action: "user_login",
    metadata: {
      email
    },
    ipAddress,
    userAgent
  });

  const destination = next ?? getDefaultPathForRole(session.role as UserRole);
  const safeDestination =
    destination && canAccessPath(session.role as UserRole, destination)
      ? destination
      : getDefaultPathForRole(session.role as UserRole);
  const sessionMaxAgeSeconds = Math.max(
    60,
    Math.floor(session.expiresAt - Math.floor(Date.now() / 1000))
  );
  const response = NextResponse.redirect(new URL(safeDestination, baseUrl));
  response.cookies.set(SESSION_COOKIE, await serializeSession(session), {
    httpOnly: true,
    sameSite: "lax",
    secure: shouldUseSecureCookies(request),
    path: "/",
    domain: getSessionCookieDomain(),
    maxAge: sessionMaxAgeSeconds
  });
  if (AUTH_DEBUG) {
    console.warn("[AUTH] session_cookie_issued", {
      userId: session.userId,
      role: session.role,
      sessionExpiresAt: session.expiresAt,
      sessionMaxAgeFromEnv: getSessionTtlSeconds(),
      maxAge: sessionMaxAgeSeconds,
      host: request.headers.get("host") || "unknown",
      protocol: request.nextUrl.protocol,
      forwardedProto: request.headers.get("x-forwarded-proto") || "unknown",
      secureCookies: shouldUseSecureCookies(request)
    });
    console.warn("[AUTH] login_success_redirect", {
      userId: session.userId,
      destination: safeDestination,
      setCookie: response.headers.get("set-cookie")
    });
  }

  response.headers.set("X-Auth-Session", "issued");
  response.headers.set("X-Auth-Session-User", session.userId);
  response.headers.set("X-Auth-Session-Role", session.role);
  response.headers.set("X-Auth-Session-Base-Url", baseUrl.toString());
  response.headers.set("X-Auth-Session-Max-Age", String(sessionMaxAgeSeconds));
  response.headers.set("X-Auth-Session-Secure", String(shouldUseSecureCookies(request)));

  return response;
}
