/**
 * Structured error logger for the admin module.
 *
 * All API route errors should use this instead of bare console.error.
 * Prints: [timestamp] [ERROR] context | message | stack | extra
 */

export interface LogExtra {
    method?: string;
    url?: string;
    params?: Record<string, unknown>;
    body?: unknown;
    [key: string]: unknown;
}

/**
 * Format and print a detailed error to stderr.
 * @param context  Short label describing where the error occurred (e.g. "GET /api/users")
 * @param error   The caught error (any type)
 * @param extra   Optional additional context (request method, url, params, etc.)
 */
export function logError(context: string, error: unknown, extra?: LogExtra): void {
    const timestamp = new Date().toISOString();

    // Extract best-effort message
    let message = "unknown error";
    if (error instanceof Error) {
        message = error.message;
    } else if (typeof error === "string") {
        message = error;
    } else if (error && typeof error === "object" && "message" in error) {
        message = String((error as { message: unknown }).message);
    }

    // Extract stack trace
    let stack = "";
    if (error instanceof Error && error.stack) {
        stack = error.stack;
    }

    const extraStr = extra
        ? ` | extra: ${JSON.stringify(redact(extra))}`
        : "";

    // Multi-line format for easy grepping
    console.error(
        `[${timestamp}] [ERROR] ${context}` +
            `\n  message: ${message}` +
            (stack ? `\n  stack: ${stack}` : "") +
            extraStr
    );
}

/** Recursively remove sensitive fields before logging */
function redact(obj: unknown, depth = 0): unknown {
    if (depth > 4) return "[max-depth]";
    if (obj === null || obj === undefined) return obj;
    if (typeof obj !== "object") return obj;
    if (Array.isArray(obj)) return obj.map((v) => redact(v, depth + 1));
    const sensitive = ["password", "token", "secret", "key", "authorization", "cookie", "api_key"];
    const result: Record<string, unknown> = {};
    for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
        if (sensitive.some((s) => k.toLowerCase().includes(s))) {
            result[k] = "[REDACTED]";
        } else {
            result[k] = redact(v, depth + 1);
        }
    }
    return result;
}
