import fs from "node:fs";
import crypto from "node:crypto";
import os from "node:os";
import path from "node:path";

import axios from "axios";
import { connect } from "nats";
import { Client as Minio } from "minio";
import PptxGenJS from "pptxgenjs";

const NATS_URL = process.env.NATS_URL || "nats://nats:4222";
const REGISTRY_URL = (process.env.ARTIFACT_REGISTRY_URL || "http://artifact-registry:9220").replace(/\/$/, "");
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || "minio:9000";
const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY || "minioadmin";
const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY || "minioadmin";
const MINIO_BUCKET = process.env.MINIO_BUCKET || "artifacts";
const MINIO_SECURE = (process.env.MINIO_SECURE || "false").toLowerCase() === "true";

const minioClient = new Minio({
  endPoint: MINIO_ENDPOINT.split(":")[0],
  port: Number(MINIO_ENDPOINT.split(":")[1] || 9000),
  useSSL: MINIO_SECURE,
  accessKey: MINIO_ACCESS_KEY,
  secretKey: MINIO_SECRET_KEY,
});

const toBuffer = async (stream) => {
  const chunks = [];
  for await (const chunk of stream) {
    chunks.push(chunk);
  }
  return Buffer.concat(chunks);
};

const sha256 = (buf) => crypto.createHash("sha256").update(buf).digest("hex");

const renderPptx = async (slidespec, outPath) => {
  const pptx = new PptxGenJS();
  pptx.layout = "LAYOUT_WIDE";
  pptx.author = "DAARION Artifact Worker";

  const slides = slidespec.slides || [];
  if (!slides.length) {
    throw new Error("No slides in slidespec");
  }

  slides.forEach((slide, idx) => {
    const s = pptx.addSlide();
    const title = slide.title || slidespec.title || `Slide ${idx + 1}`;
    s.addText(title, { x: 0.6, y: 0.6, w: 12, h: 0.8, fontSize: idx === 0 ? 36 : 28, bold: true });

    if (slide.subtitle) {
      s.addText(slide.subtitle, { x: 0.6, y: 1.6, w: 12, h: 0.5, fontSize: 16, color: "666666" });
    }

    if (slide.bullets && Array.isArray(slide.bullets) && slide.bullets.length) {
      s.addText(slide.bullets.map((b) => ({ text: b, options: { bullet: { indent: 18 } } })), {
        x: 0.8,
        y: 2.0,
        w: 11.5,
        h: 4.5,
        fontSize: 18,
        color: "333333",
      });
    }
  });

  await pptx.writeFile({ fileName: outPath });
};

const handleJob = async (msg) => {
  const data = JSON.parse(msg.data.toString());
  const { job_id, storage_key, theme_id, artifact_id, input_version_id } = data;
  const slidespecKey = storage_key || `artifacts/${artifact_id}/versions/${input_version_id}/slidespec.json`;

  try {
    const objectStream = await minioClient.getObject(MINIO_BUCKET, slidespecKey);
    const buf = await toBuffer(objectStream);
    const slidespec = JSON.parse(buf.toString("utf-8"));

    const tmpFile = path.join(os.tmpdir(), `${job_id}.pptx`);
    await renderPptx(slidespec, tmpFile, theme_id);

    const pptxBuf = fs.readFileSync(tmpFile);
    const pptxSha = sha256(pptxBuf);
    const pptxKey = `artifacts/${artifact_id}/versions/${input_version_id}/presentation.pptx`;

    await minioClient.putObject(MINIO_BUCKET, pptxKey, pptxBuf, pptxBuf.length, {
      "Content-Type": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
    });

    await axios.post(`${REGISTRY_URL}/jobs/${job_id}/complete`, {
      output_storage_key: pptxKey,
      mime: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
      size_bytes: pptxBuf.length,
      sha256: pptxSha,
      label: "pptx",
    });

    fs.unlinkSync(tmpFile);
  } catch (err) {
    await axios.post(`${REGISTRY_URL}/jobs/${job_id}/fail`, {
      error_text: String(err?.message || err),
    });
  }
};

const main = async () => {
  const nc = await connect({ servers: [NATS_URL] });
  const sub = nc.subscribe("artifact.job.render_pptx.requested");
  for await (const msg of sub) {
    await handleJob(msg);
  }
};

main().catch((err) => {
  console.error("worker failed", err);
  process.exit(1);
});
