const state = {
  running: false,
  logs: [],
  stepStatus: {
    validate: "pending",
    backup: "pending",
    upload: "pending",
    deploy: "pending"
  }
};

const versionEl = document.getElementById("version");
const zipPathEl = document.getElementById("zip-path");
const reportsPathEl = document.getElementById("reports-path");
const targetEl = document.getElementById("target");
const chooseZipButton = document.getElementById("choose-zip");
const chooseReportsButton = document.getElementById("choose-reports");
const clearReportsButton = document.getElementById("clear-reports");
const runButton = document.getElementById("run-deploy");
const stopButton = document.getElementById("stop-deploy");
const clearLogsButton = document.getElementById("clear-logs");
const logsEl = document.getElementById("logs");

document.addEventListener("DOMContentLoaded", init);

async function init() {
  bindEvents();
  await loadTargets();
  resetSteps();
  log("INFO", "Deploy Master listo.");
}

function bindEvents() {
  chooseZipButton.addEventListener("click", async () => {
    const path = await window.deployMaster.chooseZip({ title: "Seleccionar GT_ARIES.zip" });
    if (path) zipPathEl.value = path;
  });

  chooseReportsButton.addEventListener("click", async () => {
    const path = await window.deployMaster.chooseZip({ title: "Seleccionar reportes_subidos_jasper.zip" });
    if (path) reportsPathEl.value = path;
  });

  clearReportsButton.addEventListener("click", () => {
    reportsPathEl.value = "";
  });

  runButton.addEventListener("click", runDeploy);
  stopButton.addEventListener("click", stopDeploy);

  clearLogsButton.addEventListener("click", () => {
    state.logs = [];
    renderLogs();
  });

  window.deployMaster.onDeployEvent(handleDeployEvent);
}

async function loadTargets() {
  try {
    const targets = await window.deployMaster.listTargets();
    targetEl.innerHTML = targets
      .map(target => `<option value="${escapeHtml(target.id)}">${escapeHtml(target.name)} - ${escapeHtml(target.host)}:${escapeHtml(String(target.port))}</option>`)
      .join("");

    if (!targets.length) {
      targetEl.innerHTML = '<option value="">Sin destinos configurados</option>';
      log("WARN", "No hay destinos en targets.json.");
    }
  } catch (error) {
    targetEl.innerHTML = '<option value="">Error leyendo destinos</option>';
    log("ERROR", error.message || "No se pudieron cargar los destinos.");
  }
}

async function runDeploy() {
  if (state.running) return;

  resetSteps();

  const payload = {
    version: versionEl.value,
    zipPath: zipPathEl.value,
    reportsZipPath: reportsPathEl.value,
    targetId: targetEl.value
  };

  try {
    setRunning(true);
    log("INFO", "Deploy iniciado...");
    setStep("validate", "running");
    await window.deployMaster.startDeploy(payload);
  } catch (error) {
    setStep("validate", "error");
    setRunning(false);
    log("ERROR", error.message || "No se pudo iniciar el deploy.");
  }
}

async function stopDeploy() {
  await window.deployMaster.stopDeploy();
  log("WARN", "Deploy detenido por el usuario.");
}

function handleDeployEvent(event) {
  if (event.type === "stdout" || event.type === "stderr") {
    appendProcessOutput(event.text, event.type === "stderr");
  }

  if (event.type === "error") {
    setCurrentStepError();
    log("ERROR", event.text);
  }

  if (event.type === "finished") {
    if (event.code === 0) {
      Object.keys(state.stepStatus).forEach(step => setStep(step, "done"));
      log("SUCCESS", "Proceso finalizado correctamente.");
    } else if (event.code !== -1) {
      setCurrentStepError();
      log("ERROR", `El proceso termino con codigo ${event.code}.`);
    }

    setRunning(false);
  }
}

function appendProcessOutput(text, isError) {
  const lines = text.split(/\r?\n/).filter(Boolean);

  for (const line of lines) {
    const level = detectLevel(line, isError);
    log(level, line);
    updateStepsFromLine(line);
  }
}

function detectLevel(line, isError) {
  if (isError) return "ERROR";
  if (line.includes("[SUCCESS]")) return "SUCCESS";
  if (line.includes("[WARN]")) return "WARN";
  if (line.toLowerCase().includes("error")) return "ERROR";
  return "INFO";
}

function updateStepsFromLine(line) {
  if (line.includes("Validando estructura")) {
    setStep("validate", "running");
  }

  if (line.includes("Conexion SSH OK")) {
    setStep("validate", "done");
    setStep("backup", "running");
  }

  if (line.includes("Backup creado")) {
    setStep("backup", "done");
    setStep("upload", "running");
  }

  if (line.includes("ZIP principal subido")) {
    setStep("upload", "done");
    setStep("deploy", "running");
  }

  if (line.includes("Aplicacion reemplazada")) {
    setStep("deploy", "running");
  }

  if (line.includes("Deploy finalizado correctamente")) {
    setStep("deploy", "done");
  }

  if (line.toLowerCase().includes("error") || line.includes("Exception")) {
    setCurrentStepError();
  }
}

function resetSteps() {
  Object.keys(state.stepStatus).forEach(step => {
    state.stepStatus[step] = "pending";
  });
  renderSteps();
}

function setStep(step, status) {
  state.stepStatus[step] = status;
  renderSteps();
}

function setCurrentStepError() {
  const runningStep = Object.keys(state.stepStatus).find(step => state.stepStatus[step] === "running");
  if (runningStep) setStep(runningStep, "error");
}

function renderSteps() {
  document.querySelectorAll("[data-step]").forEach(element => {
    const status = state.stepStatus[element.dataset.step] || "pending";
    element.classList.remove("pending", "running", "done", "error");
    element.classList.add(status);
  });
}

function setRunning(isRunning) {
  state.running = isRunning;
  runButton.disabled = isRunning;
  versionEl.disabled = isRunning;
  chooseZipButton.disabled = isRunning;
  chooseReportsButton.disabled = isRunning;
  clearReportsButton.disabled = isRunning;
  targetEl.disabled = isRunning;
  stopButton.classList.toggle("hidden", !isRunning);
  runButton.textContent = isRunning ? "Ejecutando..." : "Ejecutar Deploy";
}

function log(level, message) {
  const normalizedLevel = level === "WARN" ? "WARNING" : level;
  state.logs.push({ level: normalizedLevel, line: message });
  renderLogs();
}

function renderLogs() {
  logsEl.innerHTML = state.logs
    .map(entry => {
      const className = entry.level === "SUCCESS"
        ? "log-success"
        : entry.level === "ERROR"
          ? "log-error"
          : entry.level === "WARNING"
            ? "log-warning"
            : "";

      return `<span class="${className}">${escapeHtml(entry.line)}</span>`;
    })
    .join("\n");

  logsEl.scrollTop = logsEl.scrollHeight;
}

function escapeHtml(value) {
  return String(value)
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;")
    .replaceAll("'", "&#039;");
}
