import { env } from "../../config/env";

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

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

function normalizeLevel(value: string): LogLevel {
  if (value === "debug" || value === "info" || value === "warn" || value === "error") {
    return value;
  }
  return "info";
}

function shouldLog(level: LogLevel) {
  return levelRank[level] >= levelRank[normalizeLevel(env.LOG_LEVEL)];
}

function serializeError(error: unknown) {
  if (!(error instanceof Error)) {
    return error;
  }
  return {
    name: error.name,
    message: error.message,
    stack: env.LOG_FORMAT === "json" ? error.stack : undefined
  };
}

export function log(level: LogLevel, event: string, fields?: Record<string, unknown>) {
  if (!shouldLog(level)) {
    return;
  }

  const payload = {
    timestamp: new Date().toISOString(),
    level,
    event,
    ...(fields || {})
  };

  if (env.LOG_FORMAT === "json") {
    const normalized = Object.fromEntries(
      Object.entries(payload).map(([key, value]) => [key, key === "error" ? serializeError(value) : value])
    );
    const line = JSON.stringify(normalized);
    if (level === "error") {
      console.error(line);
    } else if (level === "warn") {
      console.warn(line);
    } else {
      console.log(line);
    }
    return;
  }

  const suffix = fields ? ` ${JSON.stringify(fields, (_key, value) => (value instanceof Error ? serializeError(value) : value))}` : "";
  if (level === "error") {
    console.error(`[${level}] ${event}${suffix}`);
  } else if (level === "warn") {
    console.warn(`[${level}] ${event}${suffix}`);
  } else {
    console.log(`[${level}] ${event}${suffix}`);
  }
}

export const logger = {
  debug: (event: string, fields?: Record<string, unknown>) => log("debug", event, fields),
  info: (event: string, fields?: Record<string, unknown>) => log("info", event, fields),
  warn: (event: string, fields?: Record<string, unknown>) => log("warn", event, fields),
  error: (event: string, fields?: Record<string, unknown>) => log("error", event, fields)
};
