import { createHmac, timingSafeEqual } from "node:crypto";
import type { AppConfig } from "../config.js";

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

type TokenKind = "admin_access" | "admin_session";

type AdminTokenClaims = {
  aud: string;
  email: string;
  exp: number;
  role: AdminRole;
  sub: string;
  type: TokenKind;
};

export type AdminSessionClaims = {
  aud: string;
  exp: number;
  operatorEmail: string;
  operatorId: string;
  role: AdminRole;
};

type AdminTokenErrorCode =
  | "ADMIN_SESSION_REQUIRED"
  | "INVALID_ADMIN_ACCESS_TOKEN"
  | "INVALID_ADMIN_SESSION";

type AdminTokenFailure = {
  code: AdminTokenErrorCode;
  error: string;
  statusCode: 401;
  success: false;
};

type AdminTokenSuccess<T> = {
  success: true;
  value: T;
};

export type AdminTokenResult<T> = AdminTokenFailure | AdminTokenSuccess<T>;

type ExchangeResult = {
  expiresAt: string;
  operatorEmail: string;
  operatorId: string;
  role: AdminRole;
  sessionToken: string;
};

function encodeSegment(value: unknown) {
  return Buffer.from(JSON.stringify(value)).toString("base64url");
}

function decodeSegment<T>(segment: string): T | null {
  try {
    return JSON.parse(Buffer.from(segment, "base64url").toString("utf8")) as T;
  } catch {
    return null;
  }
}

function signToken(claims: AdminTokenClaims, secret: string) {
  const header = encodeSegment({ alg: "HS256", typ: "JWT" });
  const payload = encodeSegment(claims);
  const signature = createHmac("sha256", secret).update(`${header}.${payload}`).digest("base64url");

  return `${header}.${payload}.${signature}`;
}

function invalidToken(error: string, code: AdminTokenErrorCode): AdminTokenFailure {
  return {
    code,
    error,
    statusCode: 401,
    success: false,
  };
}

function isFailure<T>(result: AdminTokenResult<T>): result is AdminTokenFailure {
  return !result.success;
}

function verifyToken(
  token: string,
  config: AppConfig,
  expectedType: TokenKind
): AdminTokenResult<AdminTokenClaims> {
  const segments = token.split(".");
  if (segments.length !== 3) {
    return invalidToken(
      expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session",
      expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION"
    );
  }

  const [header, payload, signature] = segments;
  const expectedSignature = createHmac("sha256", config.ADMIN_TOKEN_SIGNING_SECRET)
    .update(`${header}.${payload}`)
    .digest();
  const receivedSignature = Buffer.from(signature, "base64url");

  if (
    expectedSignature.length !== receivedSignature.length ||
    !timingSafeEqual(expectedSignature, receivedSignature)
  ) {
    return invalidToken(
      expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session",
      expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION"
    );
  }

  const claims = decodeSegment<AdminTokenClaims>(payload);
  if (!claims) {
    return invalidToken(
      expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session",
      expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION"
    );
  }

  if (claims.type !== expectedType || claims.aud !== config.ADMIN_TOKEN_AUDIENCE) {
    return invalidToken(
      expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session",
      expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION"
    );
  }

  if (claims.exp <= Math.floor(Date.now() / 1000)) {
    return invalidToken(
      expectedType === "admin_access" ? "Invalid privileged access token" : "Invalid admin session",
      expectedType === "admin_access" ? "INVALID_ADMIN_ACCESS_TOKEN" : "INVALID_ADMIN_SESSION"
    );
  }

  return { success: true, value: claims };
}

export function exchangePrivilegedToken(
  token: string,
  config: AppConfig
): AdminTokenResult<ExchangeResult> {
  const verifiedAccessToken = verifyToken(token, config, "admin_access");
  if (isFailure(verifiedAccessToken)) {
    return verifiedAccessToken;
  }

  const accessClaims = verifiedAccessToken.value;
  const sessionExpiry = Math.floor(Date.now() / 1000) + 15 * 60;
  const sessionToken = signToken(
    {
      aud: config.ADMIN_TOKEN_AUDIENCE,
      email: accessClaims.email,
      exp: sessionExpiry,
      role: accessClaims.role,
      sub: accessClaims.sub,
      type: "admin_session",
    },
    config.ADMIN_TOKEN_SIGNING_SECRET
  );

  return {
    success: true,
    value: {
      expiresAt: new Date(sessionExpiry * 1000).toISOString(),
      operatorEmail: accessClaims.email,
      operatorId: accessClaims.sub,
      role: accessClaims.role,
      sessionToken,
    },
  };
}

export function verifyAdminSession(
  token: string | undefined,
  config: AppConfig
): AdminTokenResult<AdminSessionClaims> {
  if (!token) {
    return invalidToken("Admin session required", "ADMIN_SESSION_REQUIRED");
  }

  const verifiedSession = verifyToken(token, config, "admin_session");
  if (isFailure(verifiedSession)) {
    return {
      ...verifiedSession,
      code: "ADMIN_SESSION_REQUIRED",
      error: "Admin session required",
    };
  }

  return {
    success: true,
    value: {
      aud: verifiedSession.value.aud,
      exp: verifiedSession.value.exp,
      operatorEmail: verifiedSession.value.email,
      operatorId: verifiedSession.value.sub,
      role: verifiedSession.value.role,
    },
  };
}
