/**
 * Shared structured logger (Pino).
 *
 * Single source of truth for all server-side logging. Outputs newline-delimited
 * JSON to stdout in production. Reads `LOG_LEVEL` from env (`debug` / `info` /
 * `warn` / `error`); defaults to `debug` in development and `info` otherwise.
 *
 * Use `logger` for top-level lines and `childLogger('namespace')` for module-
 * specific lines. The active request context (`reqId`, `restaurantId`,
 * `userId`, `route`) is automatically merged into every line via the AsyncLocalStorage
 * mixin defined in `./context`.
 *
 * Sensitive fields are redacted by Pino's redaction config — never log raw
 * phone numbers, emails, tokens, secrets, or auth headers; if a partial value
 * is genuinely useful, use the helpers in `./redact`.
 *
 * NOTE: We cannot use `import 'server-only'` here even though pino is
 * Node-only. This file is loaded by the custom `tsx server.ts`
 * bootstrap as well as by Next-bundled route handlers, and the
 * `server-only` marker package always throws at runtime unless the
 * bundler swaps it for the `react-server` condition export. Plain Node
 * (tsx) does not honor that condition, so adding the import would
 * crash the custom server on boot. Callers that must run on the Edge
 * runtime or in client bundles should use `console` directly instead
 * of importing this module — see `src/middleware.ts` for an example.
 */
import pino, { type Logger, type LoggerOptions } from 'pino';
import { getRequestContext } from './context';

function resolveLevel(): string {
  const raw = (process.env.LOG_LEVEL || '').trim().toLowerCase();
  if (raw && ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent'].includes(raw)) {
    return raw;
  }
  return process.env.NODE_ENV === 'production' ? 'info' : 'debug';
}

const SENSITIVE_KEYS = [
  'api_key', 'apiKey',
  'access_token', 'accessToken',
  'refresh_token', 'refreshToken',
  'password', 'password_hash',
  'token', 'secret',
  'webhook_secret', 'webhookSecret', 'hmac_secret',
  'encrypted_api_key', 'encrypted_password', 'encrypted_token',
  'encrypted_secret', 'encrypted_refresh_token',
  'phone', 'email',
  'customer_phone', 'customer_email',
  'guest_phone', 'guest_email',
  'from', 'to',
  'authorization', 'cookie',
];

const REDACT_PATHS = [
  // Request headers
  'req.headers.authorization',
  'req.headers.cookie',
  'req.headers["x-api-key"]',
  'headers.authorization',
  'headers.cookie',
  'headers["x-api-key"]',
  // Root-level sensitive keys (caller logged the field directly).
  ...SENSITIVE_KEYS,
  // One-level nesting (e.g. ctx.email, body.token).
  ...SENSITIVE_KEYS.map((k) => `*.${k}`),
  // Two-level nesting (e.g. req.body.email, payload.customer.phone).
  ...SENSITIVE_KEYS.map((k) => `*.*.${k}`),
];

const baseOptions: LoggerOptions = {
  level: resolveLevel(),
  base: { service: 'restroagent' },
  timestamp: pino.stdTimeFunctions.isoTime,
  formatters: {
    level: (label) => ({ level: label }),
  },
  redact: {
    paths: REDACT_PATHS,
    censor: '[REDACTED]',
    remove: false,
  },
  mixin() {
    const ctx = getRequestContext();
    if (!ctx) return {};
    const out: Record<string, unknown> = {};
    if (ctx.reqId) out.reqId = ctx.reqId;
    if (ctx.restaurantId) out.restaurantId = ctx.restaurantId;
    if (ctx.userId) out.userId = ctx.userId;
    if (ctx.route) out.route = ctx.route;
    return out;
  },
};

// Pretty output is useful in dev; production stays JSON for log aggregation.
// We use pino-pretty as a *synchronous* destination stream rather than the
// worker-thread `transport` option, because Next.js can't bundle the worker
// entry point cleanly (esp. when imported from middleware.ts, which would
// otherwise crash on "Cannot find module .../vendor-chunks/lib/worker.js").
function buildLogger(): Logger {
  if (process.env.NODE_ENV !== 'production' && process.env.LOG_PRETTY !== 'false') {
    try {
      // Lazy-require so production bundles never pull pino-pretty in.
      // eslint-disable-next-line @typescript-eslint/no-require-imports
      const pretty = require('pino-pretty');
      const stream = pretty({
        colorize: true,
        translateTime: 'HH:MM:ss.l',
        ignore: 'pid,hostname,service',
        singleLine: false,
        sync: true,
      });
      return pino(baseOptions, stream);
    } catch {
      /* fall through to JSON */
    }
  }
  return pino(baseOptions);
}

export const logger: Logger = buildLogger();

const childCache = new Map<string, Logger>();

/**
 * Re-exported Pino Logger type. Callers should always use the object-first
 * signature (`log.error({ err, ...ctx }, 'message')`) or a plain string
 * message (`log.info('message')`) — those match Pino's strict typings and
 * preserve structured serialization + the redaction config above.
 */
export type ChildLogger = Logger;

export function childLogger(namespace: string): ChildLogger {
  const cached = childCache.get(namespace);
  if (cached) return cached;
  const child = logger.child({ ns: namespace });
  childCache.set(namespace, child);
  return child;
}
