import {
  ContentAuthzError,
  ContentConflictError,
  ContentNotFoundError,
  ContentValidationError,
} from '@platform-modules/content';
import { ContentMigrateError } from '@platform-modules/content/migrate';
import { SettingsValidationError } from './settings.js';

// Entity ids map to a Postgres `uuid` column — a non-UUID string must be rejected at the trust
// boundary, else it reaches the DB as a 22P02 cast error → opaque 500 (security-guard R1).
const ENTITY_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

// True iff `value` is a syntactically valid entity UUID. Shared trust-boundary guard so every admin
// route validates ids identically (a non-UUID reaching the Postgres `uuid` column is a 22P02 → 500).
export function isEntityId(value: unknown): value is string {
  return typeof value === 'string' && ENTITY_ID_RE.test(value);
}

// Validate a JSON body that carries an entity `id`; returns the validated UUID. Shared by every
// admin route that takes an id (publish/unpublish/schedule/visibility/content DELETE) so they
// reject malformed ids identically with a clean 400, not a 500.
export function parseIdBody(data: unknown): string {
  if (typeof data !== 'object' || data === null) {
    throw new ContentValidationError('input', 'must be an object');
  }
  const id = (data as { id?: unknown }).id;
  if (!isEntityId(id)) {
    throw new ContentValidationError('id', 'must be a valid UUID');
  }
  return id;
}

export function jsonError(status: number, code: string, message: string): Response {
  return new Response(JSON.stringify({ error: { code, message } }), {
    status,
    headers: { 'Content-Type': 'application/json' },
  });
}

export function jsonOk(data: unknown, status = 200): Response {
  return new Response(JSON.stringify(data), {
    status,
    headers: { 'Content-Type': 'application/json' },
  });
}

export function mapContentError(error: unknown): Response {
  const name = (error as { name?: string })?.name;
  if (name === 'CsrfError') {
    return jsonError(403, 'forbidden', (error as Error).message);
  }
  if (name === 'RateLimitedError') {
    return jsonError(429, 'rate_limited', (error as Error).message);
  }
  if (name === 'RoleDeniedError') {
    return jsonError(403, 'forbidden', (error as Error).message);
  }
  if (name === 'InvalidSessionError') {
    return jsonError(401, 'unauthorized', (error as Error).message);
  }
  if (error instanceof ContentAuthzError) {
    return jsonError(403, 'forbidden', error.message);
  }
  if (error instanceof ContentNotFoundError) {
    return jsonError(404, 'not_found', error.message);
  }
  if (error instanceof ContentConflictError) {
    return jsonError(409, 'conflict', error.message);
  }
  if (error instanceof ContentValidationError) {
    return jsonError(400, 'validation_error', error.message);
  }
  if (error instanceof ContentMigrateError) {
    return jsonError(400, 'validation_error', error.message);
  }
  if (error instanceof SettingsValidationError) {
    return jsonError(400, 'validation_error', error.message);
  }
  if (name === 'UserNotFoundError') {
    return jsonError(404, 'not_found', (error as Error).message);
  }
  if (name === 'UnknownLocaleError') {
    return jsonError(
      400,
      'validation_error',
      error instanceof Error ? error.message : 'invalid locale',
    );
  }
  if (name === 'SourceUnavailableError') {
    return jsonError(404, 'not_found', error instanceof Error ? error.message : 'source unavailable');
  }
  if (name === 'LastAdminError') {
    return jsonError(409, 'conflict', (error as Error).message);
  }
  // Notifications inbox typed input errors → client-caused 400, not a server 500.
  // (>1000 ids → InboxValidationError; malformed ?cursor= → InvalidCursorError.)
  if (name === 'InboxValidationError' || name === 'InvalidCursorError') {
    return jsonError(400, 'validation_error', (error as Error).message);
  }
  return jsonError(500, 'internal_error', 'internal error');
}
