import crypto from 'crypto';
import { prisma } from '../db/prisma';
import type { ResolvedAccess } from './auth';

// Raw keys are prefixed so the auth middleware can distinguish them from JWTs
// (which always start with "eyJ").
export const API_KEY_PREFIX = 'bbe_';
const DISPLAY_PREFIX_LENGTH = API_KEY_PREFIX.length + 8;

export function hashApiKey(rawKey: string): string {
  return crypto.createHash('sha256').update(rawKey).digest('hex');
}

// Generates a new random key. Returns the raw key (shown once), its display
// prefix, and the hash to persist.
export function generateApiKey(): { rawKey: string; prefix: string; keyHash: string } {
  const rawKey = API_KEY_PREFIX + crypto.randomBytes(32).toString('hex');
  return {
    rawKey,
    prefix: rawKey.slice(0, DISPLAY_PREFIX_LENGTH),
    keyHash: hashApiKey(rawKey),
  };
}

export function looksLikeApiKey(token: string): boolean {
  return token.startsWith(API_KEY_PREFIX);
}

// Resolves a raw API key into an access object scoped to the key's permissions.
// Returns null when the key is unknown or revoked. Identity is attributed to the
// pubkey that created the key, but the permissions come from the key, not the
// creator's role.
export async function resolveApiKey(rawKey: string): Promise<ResolvedAccess | null> {
  const keyHash = hashApiKey(rawKey);
  const key = await prisma.apiKey.findUnique({ where: { keyHash } });
  if (!key || key.revokedAt) return null;

  let permissions: string[] = [];
  try {
    const parsed = JSON.parse(key.permissions);
    if (Array.isArray(parsed)) permissions = parsed.filter((p): p is string => typeof p === 'string');
  } catch {
    permissions = [];
  }

  // Best-effort usage tracking; never block the request on it.
  prisma.apiKey
    .update({ where: { id: key.id }, data: { lastUsedAt: new Date() } })
    .catch(() => undefined);

  return {
    pubkey: key.createdByPubkey,
    role: 'guest',
    isSuperAdmin: false,
    permissions: new Set(permissions),
  };
}
