import { createHash, randomUUID } from "crypto";
import bcrypt from "bcryptjs";
import { SignJWT, jwtVerify } from "jose";
import { cookies } from "next/headers";
import { NextResponse } from "next/server";
import { query } from "./db";

export const ADMIN_COOKIE = "kumbh_admin_session";
const ADMIN_TTL_HOURS = 12;
const ADMIN_TTL = `${ADMIN_TTL_HOURS}h`;

export type AdminRole = "superadmin" | "operator" | "auditor" | "viewer";

export type AdminUser = {
  id: number;
  username: string;
  role: AdminRole;
  display_name: string;
  sessionId: string;
};

type AdminRow = {
  id: number;
  username: string;
  password_hash: string;
  display_name: string;
  role: AdminRole;
  is_active: number;
};

function getSecret() {
  return new TextEncoder().encode(
    process.env.JWT_SECRET || "kumbh-dev-secret",
  );
}

export function hashToken(token: string) {
  return createHash("sha256").update(token).digest("hex");
}

export function getBootstrapPassword() {
  return (
    process.env.ADMIN_BOOTSTRAP_PASSWORD ||
    process.env.ADMIN_PASSWORD ||
    "admin@kumbh2028"
  );
}

/** @deprecated Prefer DB login; kept for bootstrap / docs only */
export function getAdminPassword() {
  return getBootstrapPassword();
}

export async function findAdminByUsername(username: string) {
  const rows = await query<AdminRow[]>(
    `SELECT id, username, password_hash, display_name, role, is_active
     FROM admins WHERE username = :username LIMIT 1`,
    { username },
  );
  return rows[0] || null;
}

export async function verifyPassword(password: string, passwordHash: string) {
  return bcrypt.compare(password, passwordHash);
}

export async function createAdminSession(input: {
  adminId: number;
  ip?: string | null;
  userAgent?: string | null;
}) {
  const sessionId = randomUUID();
  const rawToken = randomUUID() + randomUUID();
  const tokenHash = hashToken(rawToken);
  const expiresAt = new Date(Date.now() + ADMIN_TTL_HOURS * 60 * 60 * 1000);

  await query(
    `INSERT INTO admin_sessions
      (id, admin_id, token_hash, ip, user_agent, expires_at)
     VALUES
      (:id, :admin_id, :token_hash, :ip, :user_agent,
       DATE_ADD(NOW(), INTERVAL ${ADMIN_TTL_HOURS} HOUR))`,
    {
      id: sessionId,
      admin_id: input.adminId,
      token_hash: tokenHash,
      ip: input.ip || null,
      user_agent: input.userAgent?.slice(0, 250) || null,
    },
  );

  await query(`UPDATE admins SET last_login_at = NOW() WHERE id = :id`, {
    id: input.adminId,
  });

  const jwt = await new SignJWT({
    role: "admin",
    aid: input.adminId,
  })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setJti(sessionId)
    .setExpirationTime(ADMIN_TTL)
    .sign(getSecret());

  return { sessionId, jwt, expiresAt };
}

export async function revokeAdminSession(sessionId: string) {
  await query(
    `UPDATE admin_sessions SET revoked_at = NOW()
     WHERE id = :id AND revoked_at IS NULL`,
    { id: sessionId },
  );
}

export async function requireAdmin(): Promise<AdminUser | null> {
  const jar = await cookies();
  const token = jar.get(ADMIN_COOKIE)?.value;
  if (!token) return null;

  try {
    const { payload } = await jwtVerify(token, getSecret());
    const sessionId = typeof payload.jti === "string" ? payload.jti : null;
    if (!sessionId) return null;

    const rows = await query<
      {
        session_id: string;
        admin_id: number;
        username: string;
        display_name: string;
        role: AdminRole;
        is_active: number;
        revoked_at: Date | string | null;
        expires_at: Date | string;
      }[]
    >(
      `SELECT s.id AS session_id, s.revoked_at, s.expires_at,
              a.id AS admin_id, a.username, a.display_name, a.role, a.is_active
       FROM admin_sessions s
       INNER JOIN admins a ON a.id = s.admin_id
       WHERE s.id = :id
       LIMIT 1`,
      { id: sessionId },
    );

    const row = rows[0];
    if (!row) return null;
    if (row.revoked_at) return null;
    if (new Date(row.expires_at).getTime() <= Date.now()) return null;
    if (!row.is_active) return null;

    return {
      id: row.admin_id,
      username: row.username,
      role: row.role,
      display_name: row.display_name,
      sessionId: row.session_id,
    };
  } catch {
    return null;
  }
}

export async function requireRole(roles: AdminRole[]): Promise<AdminUser | null> {
  const admin = await requireAdmin();
  if (!admin) return null;
  if (!roles.includes(admin.role)) return null;
  return admin;
}

export function forbidden() {
  return NextResponse.json(
    { ok: false, error: "Forbidden" },
    { status: 403 },
  );
}

export function unauthorized() {
  return NextResponse.json(
    { ok: false, error: "Unauthorized" },
    { status: 401 },
  );
}

export function adminCookieOptions(token: string) {
  return {
    name: ADMIN_COOKIE,
    value: token,
    httpOnly: true,
    sameSite: "lax" as const,
    secure: process.env.NODE_ENV === "production",
    path: "/",
    maxAge: ADMIN_TTL_HOURS * 60 * 60,
  };
}
