import "server-only";

import { cookies } from "next/headers";
import { cache } from "react";

import { SESSION_COOKIE, SESSION_MAX_AGE_MS } from "@/lib/auth/constants";
import {
  CmsError,
  cmsClient,
  cmsPaths,
  sessionToken,
  strOrNull,
} from "@/lib/cms/api";
import type { AdminRole } from "@/lib/cms/types";

/**
 * Sessions live on the Laravel CMS: login returns an API token, kept here in
 * an httpOnly cookie and sent as `Authorization: Bearer` on every request.
 * Laravel decides when it expires and revokes it on logout.
 */
export type CurrentAdmin = {
  id: string;
  email: string;
  name: string;
  role: AdminRole;
  avatarUrl: string | null;
};

/** `{id, email, name, role, avatarUrl}` from the CMS (`{user}` or `{data: {user}}`). */
export function toCurrentAdmin(body: unknown): CurrentAdmin | null {
  const record = (body ?? {}) as Record<string, unknown>;
  const data = record.data as Record<string, unknown> | undefined;
  const user = (record.user ??
    data?.user ??
    (data && "email" in data ? data : null)) as
    Record<string, unknown> | null | undefined;
  if (!user || typeof user !== "object" || !user.email) return null;
  return {
    id: String(user.id),
    email: String(user.email),
    name: String(user.name ?? user.email),
    role: user.role === "ADMIN" ? "ADMIN" : "EDITOR",
    avatarUrl: strOrNull(user, "avatar_url"),
  };
}

/** Stores the token Laravel issued at login. */
export async function createSession(token: string) {
  const cookieStore = await cookies();
  cookieStore.set(SESSION_COOKIE, token, {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax",
    path: "/",
    expires: new Date(Date.now() + SESSION_MAX_AGE_MS),
  });
}

/**
 * The session for this request, or null when there is no token or Laravel no
 * longer accepts it. Deduplicated per request, so layouts, pages and actions
 * can all call it freely.
 */
export const getSession = cache(async () => {
  const token = await sessionToken();
  if (!token) return null;

  try {
    const cms = await cmsClient();
    const user = toCurrentAdmin(await cms.request(cmsPaths.me));
    return user ? { user } : null;
  } catch (error) {
    // An expired or revoked token (or a redirect to Laravel's login page).
    if (
      error instanceof CmsError &&
      (error.status === 401 ||
        error.status === 403 ||
        error.status === 419 ||
        (error.status >= 300 && error.status < 400))
    ) {
      return null;
    }
    throw error;
  }
});

/** Revokes the token on the CMS and clears the cookie. */
export async function destroySession() {
  if (await sessionToken()) {
    try {
      await (await cmsClient()).request(cmsPaths.logout, { method: "POST" });
    } catch (error) {
      // Signing out locally still works when the token is already gone.
      console.warn("[auth] logout on the CMS failed", error);
    }
  }
  (await cookies()).delete(SESSION_COOKIE);
}
