const fs = require("fs");
const path = require("path");
const { getSirayaDataRoot, ensureDir } = require("./paths");
const { createLogger } = require("./logger");
const { rasterToEscPos, imageDataToRaster } = require("./escpos-raster");

const logger = createLogger();
const cache = new Map();

function getBrandingDir() {
  return path.join(getSirayaDataRoot(), "branding");
}

function getDefaultLogoPath() {
  return path.join(getBrandingDir(), "logo.png");
}

function findLogoPath() {
  const dir = getBrandingDir();
  if (!fs.existsSync(dir)) return null;
  const preferred = getDefaultLogoPath();
  if (fs.existsSync(preferred)) return preferred;
  const match = fs
    .readdirSync(dir)
    .find((f) => /^logo\.(png|jpe?g|webp)$/i.test(f));
  return match ? path.join(dir, match) : null;
}

function resolveLogoFile(template) {
  const branding = template?.branding || {};
  const raw = branding.logoPath;
  if (raw) {
    const resolved = path.isAbsolute(raw) ? raw : path.join(getSirayaDataRoot(), raw);
    if (fs.existsSync(resolved)) return resolved;
  }
  return findLogoPath();
}

function shouldShowLogo(template) {
  const cond = template?.conditionals || {};
  const branding = template?.branding || {};
  return Boolean(cond.showLogo || branding.showLogo || template?.business?.showLogo);
}

function requireJimp() {
  try {
    return require("jimp");
  } catch (e) {
    logger.error(
      `[logo] jimp is not installed — logo printing disabled. Run: cd /opt/siraya/printer && npm ci --omit=dev`,
    );
    return null;
  }
}

async function loadImageWithJimp(filePath, maxWidthPx) {
  const Jimp = requireJimp();
  if (!Jimp) return null;

  const image = await Jimp.read(filePath);
  if (image.bitmap.width > maxWidthPx) {
    image.resize(maxWidthPx, Jimp.AUTO);
  }
  image.grayscale();
  image.contrast(0.12);
  const { width, height } = image.bitmap;
  return { data: image.bitmap.data, width, height };
}

function resolveLogoScale(template) {
  const raw =
    template?.branding?.logoScale ??
    template?.visual?.logoScale ??
    100;
  return Math.max(25, Math.min(150, parseInt(String(raw), 10) || 100));
}

async function buildLogoEscPos(filePath, paperWidth = 32, logoScale = 100) {
  const { getPaperMetrics } = require("./paper-metrics");
  const scale = Math.max(25, Math.min(150, parseInt(String(logoScale), 10) || 100));
  const maxWidthPx = Math.round(getPaperMetrics(paperWidth).logoMaxPx * (scale / 100));
  const key = `${filePath}:${maxWidthPx}:${scale}`;
  if (cache.has(key)) return cache.get(key);

  if (!filePath || !fs.existsSync(filePath)) {
    cache.set(key, "");
    return "";
  }

  try {
    const bitmap = await loadImageWithJimp(filePath, maxWidthPx);
    if (!bitmap) {
      cache.set(key, "");
      return "";
    }
    const { raster, widthPx, heightPx } = imageDataToRaster(
      bitmap.data,
      bitmap.width,
      bitmap.height,
    );
    const esc = rasterToEscPos(raster, widthPx, heightPx);
    logger.info(`[logo] raster ${widthPx}x${heightPx} -> ${esc.length} ESC/POS bytes`);
    cache.set(key, esc);
    return esc;
  } catch (e) {
    logger.error(`[logo] conversion failed: ${e.message}`);
    cache.set(key, "");
    return "";
  }
}

function clearLogoCache() {
  cache.clear();
}

/** Resize and optimize logo on upload so printing stays fast. */
async function saveLogoFromBuffer(buffer) {
  ensureDir(getBrandingDir());
  const outPath = getDefaultLogoPath();
  for (const file of fs.readdirSync(getBrandingDir())) {
    if (/^logo\.(png|jpe?g|webp)$/i.test(file)) {
      fs.unlinkSync(path.join(getBrandingDir(), file));
    }
  }

  const Jimp = requireJimp();
  if (Jimp) {
    try {
      const image = await Jimp.read(buffer);
      if (image.bitmap.width > 384) {
        image.resize(384, Jimp.AUTO);
      }
      image.grayscale();
      await image.quality(90).writeAsync(outPath);
      clearLogoCache();
      logger.info(`[logo] saved optimized logo ${outPath}`);
      return outPath;
    } catch (e) {
      logger.warn(`[logo] optimize failed, saving raw: ${e.message}`);
    }
  }

  fs.writeFileSync(outPath, buffer);
  clearLogoCache();
  return outPath;
}

async function getLogoDiagnostics() {
  const logoPath = findLogoPath();
  const Jimp = requireJimp();
  let escposBytes = 0;
  if (logoPath && Jimp) {
    escposBytes = (await buildLogoEscPos(logoPath, 32)).length;
  }
  return {
    logoPath,
    logoExists: Boolean(logoPath && fs.existsSync(logoPath)),
    logoBytes: logoPath && fs.existsSync(logoPath) ? fs.statSync(logoPath).size : 0,
    jimpInstalled: Boolean(Jimp),
    escposBytes,
    printReady: escposBytes > 0,
  };
}

module.exports = {
  getBrandingDir,
  getDefaultLogoPath,
  findLogoPath,
  resolveLogoFile,
  resolveLogoScale,
  shouldShowLogo,
  buildLogoEscPos,
  clearLogoCache,
  saveLogoFromBuffer,
  getLogoDiagnostics,
};
