import "server-only";

import { createHash, randomBytes } from "node:crypto";
import { cache } from "react";
import { cookies } from "next/headers";
import { notFound, redirect } from "next/navigation";
import { canEdit, type UserRole } from "@/lib/auth/roles";
import { db } from "../db";
import { env } from "../env";
import { SESSION_COOKIE } from "./constants";

export type CurrentUser = { id: string; username: string; role: UserRole };

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

export async function createSession(userId: string): Promise<void> {
  const token = randomBytes(32).toString("base64url");
  const expiresAt = new Date(Date.now() + env.SESSION_TTL_HOURS * 60 * 60 * 1000);

  await db.session.create({
    data: { tokenHash: hashToken(token), userId, expiresAt },
  });

  const cookieStore = await cookies();
  cookieStore.set(SESSION_COOKIE, token, {
    httpOnly: true,
    secure: env.NODE_ENV === "production",
    sameSite: "lax",
    path: "/",
    expires: expiresAt,
  });
}

export async function destroySession(): Promise<void> {
  const cookieStore = await cookies();
  const token = cookieStore.get(SESSION_COOKIE)?.value;
  if (token) {
    await db.session.deleteMany({ where: { tokenHash: hashToken(token) } });
  }
  cookieStore.delete(SESSION_COOKIE);
}

// Cached per request so layouts, pages and actions share one lookup.
export const getCurrentUser = cache(async (): Promise<CurrentUser | null> => {
  const cookieStore = await cookies();
  const token = cookieStore.get(SESSION_COOKIE)?.value;
  if (!token) return null;

  const session = await db.session.findUnique({
    where: { tokenHash: hashToken(token) },
    select: { expiresAt: true, user: { select: { id: true, username: true, role: true } } },
  });

  if (!session || session.expiresAt <= new Date()) return null;
  return session.user;
});

/** Use in every protected page, layout, server action and route handler. */
export async function requireUser(): Promise<CurrentUser> {
  const user = await getCurrentUser();
  if (!user) redirect("/login");
  return user;
}

/**
 * For pages only a Super Admin may open (add / edit forms, Monitoring Settings).
 * A viewer gets the 404 page, as if the page weren't there. Server actions check
 * `canEdit()` themselves and return VIEW_ONLY_ERROR instead.
 */
export async function requireAdmin(): Promise<CurrentUser> {
  const user = await requireUser();
  if (!canEdit(user)) notFound();
  return user;
}

export async function purgeExpiredSessions(): Promise<void> {
  await db.session.deleteMany({ where: { expiresAt: { lte: new Date() } } });
}
