/**
 * API Route Definition Helper
 *
 * Validates route definitions at module load time (startup), not at request
 * time. A misconfigured route throws immediately — before any request is
 * served — making integration errors impossible to miss.
 *
 * Usage (in any handler file or module api.js):
 *   import { defineApiRoutes } from '../define.js';
 *
 *   export const routes = defineApiRoutes([
 *     { path: '/health', method: 'GET', handler: handleHealth, auth: 'public' },
 *   ]);
 *
 * Required fields per route:
 *   path           {string}   Must start with '/'. Supports ':param' and trailing '/**'.
 *   method         {string}   One of: GET | POST | PUT | PATCH | DELETE
 *   handler        {Function} Async function — signature: (request, params, context)
 *   auth           {string}   One of: 'public' | 'user' | 'admin'
 *
 * Optional fields per route:
 *   skipRateLimit  {boolean}  When true, the router skips the per-IP rate limit
 *                             check for this route. Use sparingly — only for routes
 *                             that must remain accessible under high probe frequency
 *                             (e.g. health checks from monitoring systems).
 *   permission     {string}   When set on an 'admin' route, the router additionally
 *                             verifies that the authenticated user holds this granular
 *                             permission key (e.g. 'users.manage'). If the user lacks
 *                             the permission, the request is rejected with 403 Forbidden.
 *
 * Auth levels:
 *   'public'  Anyone can call this route. context.session is undefined.
 *   'user'    Requires a valid session. context.session.user is set.
 *   'admin'   Requires a valid session with role === 'admin'.
 */

const VALID_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
const VALID_AUTH = new Set(['public', 'user', 'admin']);

/**
 * Validate and return a frozen route definition array.
 *
 * @param {Array<{path: string, method: string, handler: Function, auth: string}>} routes
 * @returns {ReadonlyArray}
 * @throws {TypeError} If any route definition is invalid
 */
export function defineApiRoutes(routes) {
  if (!Array.isArray(routes)) {
    throw new TypeError('defineApiRoutes: argument must be an array');
  }

  for (let i = 0; i < routes.length; i++) {
    const route = routes[i];
    const at = `defineApiRoutes: route[${i}]`;

    if (!route || typeof route !== 'object') {
      throw new TypeError(`${at} must be an object`);
    }
    if (typeof route.path !== 'string' || !route.path.startsWith('/')) {
      throw new TypeError(
        `${at} — "path" must be a string starting with "/", got: ${JSON.stringify(route.path)}`
      );
    }
    if (!VALID_METHODS.has(route.method)) {
      throw new TypeError(
        `${at} (${route.path}) — "method" must be one of ${[...VALID_METHODS].join(' | ')}, got: ${JSON.stringify(route.method)}`
      );
    }
    if (typeof route.handler !== 'function') {
      throw new TypeError(
        `${at} (${route.method} ${route.path}) — "handler" must be a function`
      );
    }
    if (!VALID_AUTH.has(route.auth)) {
      throw new TypeError(
        `${at} (${route.method} ${route.path}) — "auth" must be one of "public" | "user" | "admin", got: ${JSON.stringify(route.auth)}`
      );
    }
    if (route.skipRateLimit !== undefined && typeof route.skipRateLimit !== 'boolean') {
      throw new TypeError(
        `${at} (${route.method} ${route.path}) — "skipRateLimit" must be a boolean when provided, got: ${JSON.stringify(route.skipRateLimit)}`
      );
    }
    if (route.permission !== undefined && typeof route.permission !== 'string') {
      throw new TypeError(
        `${at} (${route.method} ${route.path}) — "permission" must be a string when provided, got: ${JSON.stringify(route.permission)}`
      );
    }
  }

  // Freeze to prevent accidental mutation of route definitions at runtime.
  return Object.freeze(routes.map(r => Object.freeze({ ...r })));
}
