export type LogLevel = "debug" | "info" | "warn" | "error";

export interface LogRecord {
  timestamp: string;
  level: LogLevel;
  message: string;
  service: string;
  traceId?: string;
  spanId?: string;
  requestId?: string;
  attributes: Record<string, unknown>;
}

export interface StructuredLogger {
  log(level: LogLevel, message: string, attributes?: Record<string, unknown>): void;
  debug(message: string, attributes?: Record<string, unknown>): void;
  info(message: string, attributes?: Record<string, unknown>): void;
  warn(message: string, attributes?: Record<string, unknown>): void;
  error(message: string, attributes?: Record<string, unknown>): void;
  child(attributes: Record<string, unknown>): StructuredLogger;
}

export interface StructuredLoggerOptions {
  service?: string;
  level?: LogLevel;
  now?: () => Date;
  sink?: (record: LogRecord, line: string) => void;
  redact?: readonly string[];
  attributes?: Record<string, unknown>;
}

const LEVELS: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };

function scrub(value: Record<string, unknown>, keys: Set<string>): Record<string, unknown> {
  return Object.fromEntries(
    Object.entries(value).map(([key, item]) => [
      key,
      keys.has(key.toLowerCase()) ? "[REDACTED]" : item,
    ]),
  );
}

export function createStructuredLogger(options: StructuredLoggerOptions = {}): StructuredLogger {
  const threshold = LEVELS[options.level ?? "info"];
  const now = options.now ?? (() => new Date());
  const sink = options.sink ?? ((_record, line) => console.log(line));
  const redacted = new Set(
    (options.redact ?? ["authorization", "cookie", "password", "secret", "token"]).map((key) =>
      key.toLowerCase(),
    ),
  );
  const base = { ...(options.attributes ?? {}) };
  const logger = (attributes: Record<string, unknown>): StructuredLogger => {
    const log = (level: LogLevel, message: string, values: Record<string, unknown> = {}) => {
      if (LEVELS[level] < threshold) return;
      const combined = scrub({ ...attributes, ...values }, redacted);
      const record: LogRecord = {
        timestamp: now().toISOString(),
        level,
        message,
        service: options.service ?? "wrnexus",
        ...(typeof combined.traceId === "string" ? { traceId: combined.traceId } : {}),
        ...(typeof combined.spanId === "string" ? { spanId: combined.spanId } : {}),
        ...(typeof combined.requestId === "string" ? { requestId: combined.requestId } : {}),
        attributes: combined,
      };
      sink(record, JSON.stringify(record));
    };
    return {
      log,
      debug: (message, values) => log("debug", message, values),
      info: (message, values) => log("info", message, values),
      warn: (message, values) => log("warn", message, values),
      error: (message, values) => log("error", message, values),
      child: (values) => logger({ ...attributes, ...values }),
    };
  };
  return logger(base);
}
