const fs = require("fs");
const path = require("path");
const date = require("date-and-time");

const LOG_TS_FORMAT = "YYYY-MM-DD HH:mm:ss";

function getTimestamp() {
  return date.format(new Date(), LOG_TS_FORMAT);
}

function writeLine(filePath, line) {
  fs.appendFileSync(filePath, `${line}\n`);
}

function stringifyMeta(meta) {
  if (!meta || Object.keys(meta).length === 0) {
    return "";
  }

  return (
    " | " +
    Object.entries(meta)
      .filter(
        ([, value]) => value !== undefined && value !== null && value !== "",
      )
      .map(
        ([key, value]) =>
          `${key}=${Array.isArray(value) ? value.join(",") : value}`,
      )
      .join(" | ")
  );
}

function formatSettingValueForLog(value) {
  if (value === undefined || value === null) {
    return "<null>";
  }

  if (value === "") {
    return "<empty>";
  }

  return value;
}

function stripControlCharacters(value) {
  return String(value ?? "")
    .replace(/\x1B\x4D[\x00-\xFF]?/g, "")
    .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
}

function formatDateTime(dateInput) {
  if (!dateInput) return "";

  const parsedDate = new Date(dateInput);
  if (isNaN(parsedDate)) return "";

  const day = String(parsedDate.getDate()).padStart(2, "0");
  const month = String(parsedDate.getMonth() + 1).padStart(2, "0");
  const year = parsedDate.getFullYear();
  const hours = String(parsedDate.getHours()).padStart(2, "0");
  const minutes = String(parsedDate.getMinutes()).padStart(2, "0");
  const seconds = String(parsedDate.getSeconds()).padStart(2, "0");

  return `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`;
}

function padRight(text, width) {
  const value = String(text);
  return value + " ".repeat(Math.max(0, width - value.length));
}

function padLeft(text, width) {
  const value = String(text);
  return " ".repeat(Math.max(0, width - value.length)) + value;
}

function summarizeAction(data) {
  return {
    action: data.action,
    orderId: data.order?.id,
    table: data.order?.table?.name,
    printerIp: data.printerIp,
    debug: data.debug === true ? "true" : undefined,
    items: Array.isArray(data.order?.items)
      ? data.order.items.length
      : undefined,
    specials: Array.isArray(data.order?.specials)
      ? data.order.specials.length
      : undefined,
    settings: Array.isArray(data.settings) ? data.settings.length : undefined,
  };
}

function createServiceUtils(baseDir) {
  const logsDir = path.join(baseDir, "logs");
  fs.mkdirSync(logsDir, { recursive: true });

  const logFilePath = path.join(
    logsDir,
    `debug_${date.format(new Date(), "YYYYMMDD")}.log`,
  );
  const commandFilePath = path.join(
    logsDir,
    `commands_${date.format(new Date(), "YYYYMMDD")}.log`,
  );
  const previewFilePath = path.join(
    logsDir,
    `preview_${date.format(new Date(), "YYYYMMDD")}.log`,
  );

  function logEvent(level, message, meta = {}) {
    const line = `[${getTimestamp()}] [${level}] ${message}${stringifyMeta(meta)}`;

    if (level === "ERROR") {
      console.error(line);
    } else if (level === "WARN") {
      console.warn(line);
    } else {
      console.log(line);
    }

    writeLine(logFilePath, line);
  }

  function logCommand(action, meta = {}) {
    writeLine(
      commandFilePath,
      `[${getTimestamp()}] action=${action}${stringifyMeta(meta)}`,
    );
  }

  function logPrintSettings(scope, settings) {
    const printableSettings = Object.fromEntries(
      Object.entries(settings).map(([key, value]) => [
        key,
        formatSettingValueForLog(value),
      ]),
    );

    logEvent("INFO", `${scope} print settings`, printableSettings);
  }

  function logPreview(action, output, meta = {}) {
    const header = `[${getTimestamp()}] action=${action}${stringifyMeta(meta)}`;
    writeLine(previewFilePath, header);
    writeLine(previewFilePath, stripControlCharacters(output));
    writeLine(previewFilePath, "");
  }

  return {
    logEvent,
    logCommand,
    logPrintSettings,
    logPreview,
  };
}

module.exports = {
  createServiceUtils,
  formatDateTime,
  padLeft,
  padRight,
  summarizeAction,
};