const util = require("util");
const { db } = require("./db");

const LEVELS = new Set(["debug", "info", "warn", "error"]);
let consoleHooked = false;

function log(level, ...args) {
  const safeLevel = LEVELS.has(level) ? level : "info";
  const entry = normalizeArgs(args);
  const createdAt = Date.now();
  try {
    db.prepare(
      "INSERT INTO logs (level, message, details, created_at) VALUES (?, ?, ?, ?)"
    ).run(safeLevel, entry.message, entry.details, createdAt);
  } catch {
    // Avoid throwing from logger.
  }
}

function listLogs(options = {}) {
  const limit =
    Number.isFinite(options.limit) && options.limit !== null
      ? Math.max(1, options.limit)
      : null;
  const sinceMs =
    Number.isFinite(options.sinceMs) && options.sinceMs > 0
      ? options.sinceMs
      : null;
  const levels = Array.isArray(options.levels)
    ? options.levels.filter((level) => LEVELS.has(level))
    : [];

  const clauses = [];
  const params = [];
  if (sinceMs) {
    clauses.push("created_at >= ?");
    params.push(sinceMs);
  }
  if (levels.length) {
    clauses.push(`level IN (${levels.map(() => "?").join(",")})`);
    params.push(...levels);
  }

  let query =
    "SELECT id, level, message, details, created_at FROM logs";
  if (clauses.length) {
    query += ` WHERE ${clauses.join(" AND ")}`;
  }
  query += " ORDER BY created_at DESC";
  if (limit) {
    query += " LIMIT ?";
    params.push(limit);
  }

  return db.prepare(query).all(...params);
}

function hookConsole() {
  if (consoleHooked) {
    return;
  }
  consoleHooked = true;

  const original = {
    log: console.log,
    info: console.info || console.log,
    warn: console.warn || console.log,
    error: console.error || console.log
  };

  console.log = (...args) => {
    log("info", ...args);
    original.log.apply(console, args);
  };
  console.info = (...args) => {
    log("info", ...args);
    original.info.apply(console, args);
  };
  console.warn = (...args) => {
    log("warn", ...args);
    original.warn.apply(console, args);
  };
  console.error = (...args) => {
    log("error", ...args);
    original.error.apply(console, args);
  };
}

function normalizeArgs(args) {
  if (!args || args.length === 0) {
    return { message: "Log entry", details: "" };
  }
  let message = "";
  const detailParts = [];

  const first = args[0];
  if (first instanceof Error) {
    message = first.message || "Error";
    detailParts.push(first.stack || String(first));
  } else {
    message = formatArg(first);
  }

  for (const arg of args.slice(1)) {
    if (arg instanceof Error) {
      detailParts.push(arg.stack || arg.message || String(arg));
      if (!message) {
        message = arg.message || "Error";
      }
    } else {
      detailParts.push(formatArg(arg));
    }
  }

  if (!message) {
    message = "Log entry";
  }

  return {
    message,
    details: detailParts.filter(Boolean).join("\n")
  };
}

function formatArg(value) {
  if (typeof value === "string") {
    return value;
  }
  if (value instanceof Error) {
    return value.stack || value.message || String(value);
  }
  return util.inspect(value, {
    depth: 4,
    maxArrayLength: 50,
    breakLength: 120
  });
}

module.exports = {
  log,
  listLogs,
  hookConsole
};
