// src/web/ui/v2/static/js/pages/calibration_strategies.js

console.log("[calibration_strategies] script loaded ✅", new Date().toISOString());

let STRATEGY_CATALOG = [];
let strategySlots = [];
const MAX_STRATEGIES = 10;

// =================================================
// WIZARD NAVIGATION
// =================================================

function enableNextStep() {
  const btn = document.getElementById("next-step-btn");
  if (!btn) return;
  btn.classList.remove("btn-outline-secondary");
  btn.classList.add("btn-outline-primary");
  btn.setAttribute("aria-disabled", "false");
}

function disableNextStep() {
  const btn = document.getElementById("next-step-btn");
  if (!btn) return;
  btn.classList.remove("btn-outline-primary");
  btn.classList.add("btn-outline-secondary");
  btn.setAttribute("aria-disabled", "true");
}

// =================================================
// UTILS
// =================================================

function loadContextFromLocalStorage() {
  const symbol = localStorage.getItem("calibration.symbol");
  const timeframe = localStorage.getItem("calibration.timeframe");
  if (symbol) setVal("symbol", symbol);
  if (timeframe) setVal("timeframe", timeframe);

  // Step 2 snapshot (if stored)
  const stop_type = localStorage.getItem("calibration.stop.type");
  const stop_fraction = localStorage.getItem("calibration.stop.stop_fraction");
  const atr_period = localStorage.getItem("calibration.stop.atr_period");
  const atr_multiplier = localStorage.getItem("calibration.stop.atr_multiplier");
  const risk_fraction = localStorage.getItem("calibration.risk.risk_fraction");
  const max_position_fraction = localStorage.getItem("calibration.risk.max_position_fraction");
  const max_drawdown_pct = localStorage.getItem("calibration.rules.max_drawdown_pct");
  const daily_loss_limit_pct = localStorage.getItem("calibration.rules.daily_loss_limit_pct");
  const max_consecutive_losses = localStorage.getItem("calibration.rules.max_consecutive_losses");
  const cooldown_bars = localStorage.getItem("calibration.rules.cooldown_bars");
  const account_equity = localStorage.getItem("calibration.account_equity");

  if (account_equity) setVal("account_equity", account_equity);

  if (stop_type) setVal("stop_type", stop_type);
  if (stop_fraction) setVal("stop_fraction", stop_fraction);
  if (atr_period) setVal("atr_period", atr_period);
  if (atr_multiplier) setVal("atr_multiplier", atr_multiplier);
  if (risk_fraction) setVal("risk_fraction", risk_fraction);
  if (max_position_fraction) setVal("max_position_fraction", max_position_fraction);
  if (max_drawdown_pct) setVal("max_drawdown_pct", max_drawdown_pct);
  if (daily_loss_limit_pct) setVal("daily_loss_limit_pct", daily_loss_limit_pct);
  if (max_consecutive_losses) setVal("max_consecutive_losses", max_consecutive_losses);
  if (cooldown_bars) setVal("cooldown_bars", cooldown_bars);
}

function buildPayload() {
  const symbol = str("symbol");
  const timeframe = str("timeframe");
  const stopType = str("stop_type");

  if (!symbol || !timeframe) {
    throw new Error("symbol/timeframe missing");
  }

  const stop = { type: stopType };

  if (stopType === "fixed" || stopType === "trailing") {
    stop.stop_fraction = (num("stop_fraction") ?? 1.0) / 100;
  }
  if (stopType === "atr") {
    stop.atr_period = num("atr_period") ?? 14;
    stop.atr_multiplier = num("atr_multiplier") ?? 3.0;
  }

  const risk_fraction = (num("risk_fraction") ?? 1.0) / 100;
  const max_position_fraction = (num("max_position_fraction") ?? 95) / 100;

  const global_rules = {
    max_drawdown_pct: (num("max_drawdown_pct") ?? 20) / 100,
    daily_loss_limit_pct: num("daily_loss_limit_pct") ? num("daily_loss_limit_pct") / 100 : null,
    max_consecutive_losses: num("max_consecutive_losses"),
    cooldown_bars: num("cooldown_bars"),
  };

  const wf_train_days = num("wf_train_days") ?? 120;
  const wf_test_days = num("wf_test_days") ?? 30;
  const wf_step_days = num("wf_step_days");

  const strategies = collectSelectedStrategies();

  return {
    symbol,
    timeframe,
    account_equity: num("account_equity") ?? 10000,

    stop,
    risk: {
      risk_fraction,
      max_position_fraction,
    },
    global_rules,

    strategies,
    wf: {
      train_days: wf_train_days,
      test_days: wf_test_days,
      step_days: wf_step_days,
    },
    optimization: {
      optimizer_metric: str("opt_metric") ?? "sharpe_ratio",
      max_combinations: num("opt_max_combinations") ?? 300,
      min_trades_train: num("opt_min_trades_train") ?? 30,
      min_trades_test: num("opt_min_trades_test") ?? 10,
    },

    commission: num("commission") ?? 0.001,
    slippage: num("slippage") ?? 0.0005,
  };
}

function collectSelectedStrategies() {

  const strategies = [];

  strategySlots.forEach((slot, index) => {

    if (!slot.strategy_id) return;

    const strategyMeta = STRATEGY_CATALOG.find(
      s => s.strategy_id === slot.strategy_id
    );

    const parameters = {};

    strategyMeta.params.forEach(paramName => {

      const min = parseFloat(
        document.getElementById(`${paramName}_min_${index}`)?.value
      );

      const max = parseFloat(
        document.getElementById(`${paramName}_max_${index}`)?.value
      );

      const step = parseFloat(
        document.getElementById(`${paramName}_step_${index}`)?.value
      );

      parameters[paramName] = {
        min: min,
        max: max,
        step: step
      };
    });

    strategies.push({
      strategy_id: slot.strategy_id,
      parameters: parameters
    });
  });

  return strategies;
}

async function fetchAvailableStrategies() {
  const res = await fetch("/api/v2/calibration/strategies/catalog");
  const data = await res.json();
  return data.strategies || [];
}

function num(id) {
  const el = document.getElementById(id);
  if (!el) return null;
  const val = el.value;
  if (val === "" || val === null || val === undefined) return null;
  const n = Number(val);
  return Number.isFinite(n) ? n : null;
}

function str(id) {
  const el = document.getElementById(id);
  if (!el) return null;
  const v = el.value;
  return v === null || v === undefined ? null : String(v);
}

function setVal(id, value) {
  const el = document.getElementById(id);
  if (!el) return;
  el.value = value ?? "";
}

function loadFromStep2() {

  document.getElementById("risk_fraction").value =
    localStorage.getItem("calibration.risk_fraction") ?? "";

  document.getElementById("max_position_fraction").value =
    localStorage.getItem("calibration.max_position_fraction") ?? "";

  document.getElementById("stop_type").value =
    localStorage.getItem("calibration.stop_type") ?? "fixed";

  document.getElementById("stop_fraction").value =
    localStorage.getItem("calibration.stop_fraction") ?? "";

  document.getElementById("atr_period").value =
    localStorage.getItem("calibration.atr_period") ?? "";

  document.getElementById("atr_multiplier").value =
    localStorage.getItem("calibration.atr_multiplier") ?? "";

  document.getElementById("max_drawdown_pct").value =
    localStorage.getItem("calibration.max_drawdown_pct") ?? "";

  document.getElementById("daily_loss_limit_pct").value =
    localStorage.getItem("calibration.daily_loss_limit_pct") ?? "";

  document.getElementById("max_consecutive_losses").value =
    localStorage.getItem("calibration.max_consecutive_losses") ?? "";

  document.getElementById("cooldown_bars").value =
    localStorage.getItem("calibration.cooldown_bars") ?? "";

  // Forzar actualización de UI según stop heredado
  setTimeout(() => {
    updateStopUI();
  }, 0);

  console.log("[calibration_strategies] Parameters loaded from Step 2 ✅");
}

function updateStopUI() {

  const type = document.getElementById("stop_type").value;

  const stopFraction = document.getElementById("stop_fraction_group");
  const atrPeriod = document.getElementById("atr_group");
  const atrMultiplier = document.getElementById("atr_multiplier_group");

  if (type === "fixed" || type === "trailing") {
    stopFraction.classList.remove("d-none");
    atrPeriod.classList.add("d-none");
    atrMultiplier.classList.add("d-none");
  }

  if (type === "atr") {
    stopFraction.classList.add("d-none");
    atrPeriod.classList.remove("d-none");
    atrMultiplier.classList.remove("d-none");
  }
}

async function loadStrategyCatalog() {

  const res = await fetch("/api/v2/calibration/strategies/catalog");
  const data = await res.json();

  STRATEGY_CATALOG = data.strategies;
}

function addStrategySlot() {

  // Si ya hay un slot vacío al final, no crear otro
  if (strategySlots.length > 0 &&
      strategySlots[strategySlots.length - 1].strategy_id === null) {
    return;
  }

  if (strategySlots.length >= MAX_STRATEGIES) return;

  const index = strategySlots.length;

  strategySlots.push({
    strategy_id: null,
    parameters: {}
  });

  renderStrategySlot(index);
}

function renderStrategySlot(index) {

  const container = document.getElementById("strategies_container");

  const slot = document.createElement("div");
  slot.className = "card p-3";
  slot.id = `strategy_slot_${index}`;

  slot.innerHTML = `
    <div class="mb-3">
      <label class="form-label">Strategy ${index + 1}</label>
      <select class="form-select" id="strategy_select_${index}">
        <option value="">None</option>
        ${STRATEGY_CATALOG.map(s =>
          `<option value="${s.strategy_id}">${s.name}</option>`
        ).join("")}
      </select>
    </div>

    <div id="strategy_params_${index}" class="row g-3"></div>
    <div class="mt-2 text-end">
      <small class="text-muted">
        Combinations:
        <span id="strategy_combo_${index}">0</span>
      </small>
    </div>
  `;

  container.appendChild(slot);

  document
    .getElementById(`strategy_select_${index}`)
    .addEventListener("change", (e) => {
      onStrategySelected(index, e.target.value);
    });
}

function onStrategySelected(index, strategyId) {

  if (!strategyId) {
    removeStrategySlot(index);
    return;
  }

  strategySlots[index].strategy_id = strategyId;

  renderParametersOnly(index, strategyId);

  // Si es el último slot activo, añadir nuevo vacío
  if (index === strategySlots.length - 1 &&
      strategySlots.length < MAX_STRATEGIES) {

    strategySlots.push({ strategy_id: null, parameters: {} });
    renderStrategySlot(strategySlots.length - 1);
  }

  updateCombinationCounter();
}

function validateParameterInputs() {

  let valid = true;

  document.querySelectorAll(".param-input").forEach(input => {
    input.classList.remove("is-invalid");
  });

  strategySlots.forEach((slot, index) => {

    if (!slot.strategy_id) return;

    const strategyMeta = STRATEGY_CATALOG.find(
      s => s.strategy_id === slot.strategy_id
    );

    strategyMeta.params.forEach(paramName => {

      const minEl = document.getElementById(`${paramName}_min_${index}`);
      const maxEl = document.getElementById(`${paramName}_max_${index}`);
      const stepEl = document.getElementById(`${paramName}_step_${index}`);

      const min = parseFloat(minEl?.value);
      const max = parseFloat(maxEl?.value);
      const step = parseFloat(stepEl?.value);

      if (max < min) {
        maxEl.classList.add("is-invalid");
        valid = false;
      }

      if (step <= 0) {
        stepEl.classList.add("is-invalid");
        valid = false;
      }

    });
  });

  updateCombinationCounter();

  return valid;
}

function updateCombinationCounter() {

  let globalTotal = 1;
  let hasAnyStrategy = false;

  strategySlots.forEach((slot, index) => {

    if (!slot.strategy_id) return;

    hasAnyStrategy = true;

    const strategyMeta = STRATEGY_CATALOG.find(
      s => s.strategy_id === slot.strategy_id
    );

    let strategyTotal = 1;

    strategyMeta.params.forEach(paramName => {

      const min = parseFloat(
        document.getElementById(`${paramName}_min_${index}`)?.value
      );

      const max = parseFloat(
        document.getElementById(`${paramName}_max_${index}`)?.value
      );

      const step = parseFloat(
        document.getElementById(`${paramName}_step_${index}`)?.value
      );

      if (isNaN(min) || isNaN(max) || isNaN(step)) return;

      if (min === max || step == 0) {
        strategyTotal *= 1;
      } else {
        const count = Math.floor((max - min) / step) + 1;
        strategyTotal *= Math.max(count, 1);
      }

    });

    const perStrategyEl = document.getElementById(`strategy_combo_${index}`);
    if (perStrategyEl) {
      perStrategyEl.textContent = strategyTotal;
    }

    globalTotal *= strategyTotal;
  });

  if (!hasAnyStrategy) globalTotal = 0;

  const globalEl = document.getElementById("combination_counter");
  if (globalEl) globalEl.textContent = globalTotal;

  applyCombinationWarnings(globalTotal);
  updateTimeEstimate(globalTotal);

  return globalTotal;
}

function applyCombinationWarnings(total) {

  const maxComb = parseInt(
    document.getElementById("opt_max_combinations")?.value || 0
  );

  const counter = document.getElementById("combination_counter");
  if (!counter) return;

  counter.classList.remove("text-warning", "text-danger");

  if (total > 10000) {
    counter.classList.add("text-danger");
  } else if (maxComb && total > maxComb) {
    counter.classList.add("text-warning");
  }
}

function updateTimeEstimate(totalComb) {

  const trainDays = parseInt(
    document.getElementById("wf_train_days")?.value || 0
  );

  const testDays = parseInt(
    document.getElementById("wf_test_days")?.value || 0
  );

  const approxWindows = Math.max(
    Math.floor(365 / testDays),
    1
  );

  const operations = totalComb * approxWindows;

  // 0.003s por combinación (estimación conservadora)
  const seconds = operations * 0.003;

  let label;

  if (seconds < 60) {
    label = `~ ${seconds.toFixed(1)} sec`;
  } else if (seconds < 3600) {
    label = `~ ${(seconds / 60).toFixed(1)} min`;
  } else {
    label = `~ ${(seconds / 3600).toFixed(1)} h`;
  }

  const el = document.getElementById("wf_time_estimate");
  if (el) el.textContent = label;
}

function removeStrategySlot(index) {

  strategySlots.splice(index, 1);

  rerenderStrategySlots();
}

function rerenderStrategySlots() {

  const container = document.getElementById("strategies_container");
  container.innerHTML = "";

  const currentStrategies = strategySlots
    .filter(s => s.strategy_id !== null);

  strategySlots = [];

  currentStrategies.forEach((slotData, index) => {

    strategySlots.push({
      strategy_id: slotData.strategy_id,
      parameters: {}
    });

    renderStrategySlot(index);

    const select = document.getElementById(`strategy_select_${index}`);
    select.value = slotData.strategy_id;

    renderParametersOnly(index, slotData.strategy_id);
  });

  // Siempre añadir un slot vacío al final
  if (strategySlots.length < MAX_STRATEGIES) {
    strategySlots.push({ strategy_id: null, parameters: {} });
    renderStrategySlot(strategySlots.length - 1);
  }

  updateCombinationCounter();
}

function renderParametersOnly(index, strategyId) {

  const paramsContainer = document.getElementById(`strategy_params_${index}`);
  paramsContainer.innerHTML = "";

  if (!strategyId) return;

  const strategyMeta = STRATEGY_CATALOG.find(
    s => s.strategy_id === strategyId
  );

  if (!strategyMeta) return;

  strategyMeta.params.forEach(paramName => {

    const col = document.createElement("div");
    col.className = "col-md-4";

    col.innerHTML = `
      <label class="form-label fw-semibold">${paramName}</label>
      <div class="row g-2">
        <div class="col">
          <small class="text-muted">Min</small>
          <input type="number"
            class="form-control param-input"
            id="${paramName}_min_${index}">
        </div>
        <div class="col">
          <small class="text-muted">Max</small>
          <input type="number"
            class="form-control param-input"
            id="${paramName}_max_${index}">
        </div>
        <div class="col">
          <small class="text-muted">Step</small>
          <input type="number"
            class="form-control param-input"
            id="${paramName}_step_${index}">
        </div>
      </div>
    `;

    paramsContainer.appendChild(col);
  });
}

// =================================================
// PROGRESS BAR
// =================================================

function startWF() {
  document
    .getElementById("wf_progress_card")
    .classList.remove("d-none");

  document.getElementById("wfProgressBar").style.width = "0%";
  document.getElementById("wfProgressBar").innerText = "0%";
}

async function pollStatus(jobId) {
  const interval = setInterval(async () => {
    const res = await fetch(`/api/v2/calibration/strategies/status/${jobId}`);
    const data = await res.json();

    const bar = document.getElementById("wfProgressBar");
    bar.style.width = data.progress + "%";
    bar.innerText = data.progress + "%";

    if (data.status === "done") {
      clearInterval(interval);
      bar.classList.remove("progress-bar-animated");
      console.log("WF finished");
    }
  }, 1000);
}

// =================================================
// RENDER RESULTS
// =================================================

function renderStrategiesList(strategies) {
  const list = document.getElementById("strategies_list");
  if (!list) return;

  list.innerHTML = "";

  strategies.forEach((s) => {
    const col = document.createElement("div");
    col.className = "col-12 col-lg-6";
    col.setAttribute("data-strategy-item", "1");

    const defaultGrid = s.default_grid || {};
    const defaultGridText = JSON.stringify(defaultGrid, null, 2);

    col.innerHTML = `
      <div class="card">
        <div class="card-body">
          <label class="form-check form-switch">
            <input class="form-check-input" type="checkbox" data-strategy-id="${s.strategy_id}">
            <span class="form-check-label">
              <b>${s.name}</b>
              <span class="text-secondary ms-2">${(s.tags || []).join(" · ")}</span>
            </span>
          </label>

          <div class="mt-3">
            <label class="form-label">param_grid (JSON)</label>
            <textarea class="form-control" rows="7" spellcheck="false">${defaultGridText}</textarea>
            <div class="form-hint">Tip: usa listas. Ej: {"fast":[10,20],"slow":[50,100]}</div>
          </div>
        </div>
      </div>
    `;

    list.appendChild(col);
  });
}

function setBadge(status) {
  const badge = document.getElementById("strategies_status_badge");
  if (!badge) return;

  badge.classList.remove("bg-secondary", "bg-success", "bg-warning", "bg-danger");
  badge.classList.add(
    status === "ok" ? "bg-success" : status === "warning" ? "bg-warning" : status === "fail" ? "bg-danger" : "bg-secondary"
  );
  badge.textContent = status ? status.toUpperCase() : "—";
}

function renderResultsTable(data) {
  const wrap = document.getElementById("strategies_table_wrap");
  if (!wrap) return;

  const rows = [];
  (data.results || []).forEach((r) => {
    rows.push(`
      <tr>
        <td><b>${r.strategy_id}</b></td>
        <td>${r.status}</td>
        <td>${r.n_windows}</td>
        <td>${Number(r.oos_total_return_pct).toFixed(2)}%</td>
        <td>${Number(r.oos_max_dd_worst_pct).toFixed(2)}%</td>
        <td>${Number(r.oos_final_equity).toFixed(2)}</td>
        <td class="text-secondary">${r.message || ""}</td>
      </tr>
    `);
  });

  wrap.innerHTML = `
    <div class="table-responsive">
      <table class="table table-vcenter">
        <thead>
          <tr>
            <th>Strategy</th>
            <th>Status</th>
            <th>Windows</th>
            <th>OOS return</th>
            <th>Worst DD</th>
            <th>Final equity</th>
            <th>Message</th>
          </tr>
        </thead>
        <tbody>
          ${rows.join("")}
        </tbody>
      </table>
    </div>
  `;
}

function populatePlotSelector(data) {
  const sel = document.getElementById("plot_strategy_select");
  if (!sel) return;

  sel.innerHTML = "";
  const ids = Object.keys((data.series && data.series.strategies) ? data.series.strategies : {});
  ids.forEach((sid) => {
    const opt = document.createElement("option");
    opt.value = sid;
    opt.textContent = sid;
    sel.appendChild(opt);
  });

  sel.onchange = () => renderPlotsForSelected(data);

  if (ids.length > 0) {
    sel.value = ids[0];
  }
}

function renderPlotsForSelected(data) {
  const sel = document.getElementById("plot_strategy_select");
  const sid = sel ? sel.value : null;
  if (!sid) return;

  const s = data.series?.strategies?.[sid];
  if (!s) return;

  const equity = s.window_equity || [];
  const returns = s.window_returns_pct || [];
  const xEq = [...Array(equity.length).keys()];
  const xRet = [...Array(returns.length).keys()].map((i) => i + 1);

  Plotly.newPlot("plot_equity", [
    { x: xEq, y: equity, type: "scatter", mode: "lines", name: "Equity (OOS)" },
  ], {
    title: `WF OOS equity · ${sid}`,
    margin: { t: 40, l: 50, r: 20, b: 40 },
    xaxis: { title: "Window index" },
    yaxis: { title: "Equity" },
  }, { displayModeBar: false });

  Plotly.newPlot("plot_returns", [
    { x: xRet, y: returns, type: "bar", name: "Return % (per window)" },
  ], {
    title: `WF returns per window · ${sid}`,
    margin: { t: 40, l: 50, r: 20, b: 40 },
    xaxis: { title: "Window" },
    yaxis: { title: "Return (%)" },
  }, { displayModeBar: false });
}

function renderValidateResponse(data) {

  // -------------------------------
  // 1️⃣ Badge + message
  // -------------------------------
  const badge = document.getElementById("strategies_status_badge");
  const msg = document.getElementById("strategies_message");

  badge.textContent = data.status ?? "—";

  badge.className = "badge";
  if (data.status === "ok") badge.classList.add("bg-success");
  else if (data.status === "warning") badge.classList.add("bg-warning");
  else badge.classList.add("bg-danger");

  msg.textContent = data.message ?? "";

  // -------------------------------
  // 2️⃣ Debug JSON
  // -------------------------------
  document.getElementById("strategies_debug").textContent =
    JSON.stringify(data, null, 2);

  // -------------------------------
  // 3️⃣ Plots (primera estrategia por ahora)
  // -------------------------------
  if (data.series && data.series.strategies) {
    const keys = Object.keys(data.series.strategies);
    if (keys.length > 0) {
      const s = data.series.strategies[keys[0]];

      Plotly.newPlot("plot_equity", [{
        y: s.window_equity,
        type: "scatter",
        mode: "lines",
        name: "Equity"
      }], { margin: { t: 20 } });

      Plotly.newPlot("plot_returns", [{
        y: s.window_returns_pct,
        type: "bar",
        name: "Window returns %"
      }], { margin: { t: 20 } });
    }
  }

  // -------------------------------
  // 4️⃣ Table
  // -------------------------------
  const wrap = document.getElementById("strategies_table_wrap");
  wrap.innerHTML = "";

  if (data.results) {
    let html = `<table class="table table-sm table-bordered">
      <thead>
        <tr>
          <th>Strategy</th>
          <th>Status</th>
          <th>OOS Return %</th>
          <th>OOS Max DD %</th>
          <th>Windows</th>
        </tr>
      </thead>
      <tbody>`;

    for (const r of data.results) {
      html += `
        <tr>
          <td>${r.strategy_id}</td>
          <td>${r.status}</td>
          <td>${r.oos_total_return_pct?.toFixed(2)}</td>
          <td>${r.oos_max_dd_worst_pct?.toFixed(2)}</td>
          <td>${r.n_windows}</td>
        </tr>
      `;
    }

    html += "</tbody></table>";
    wrap.innerHTML = html;
  }
}

async function validateStrategies() {
  console.log("[calibration_strategies] validateStrategies() START");

  const bar = document.getElementById("wfProgressBar");
  const txt = document.getElementById("wf_progress_text");

  const setProgress = (pct, text) => {
    const p = Math.max(0, Math.min(100, Number(pct || 0)));
    bar.style.width = `${p}%`;
    bar.textContent = `${p}%`;
    if (text) txt.textContent = text;
  };

  if (!validateParameterInputs()) {
    alert("Please fix parameter errors before running WF.");
    return;
  }


  try {
    // 0) Reset UI
    setProgress(0, "Starting...");

    // 1) Construye payload igual que antes (usa tu función existente)
    const payload = buildPayload(); // <-- NO CAMBIES tu builder, reutilízalo

    // 2) Arranca job async
    const runResp = await fetch("/api/v2/calibration/strategies/run", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload),
    });

    if (!runResp.ok) {
      const errText = await runResp.text();
      throw new Error(`Run failed: ${runResp.status} ${errText}`);
    }

    const { job_id } = await runResp.json();
    if (!job_id) throw new Error("No job_id returned from /run");

    // 3) Poll status
    const pollEveryMs = 500;
    const maxMinutes = 30;
    const maxPolls = Math.ceil((maxMinutes * 60 * 1000) / pollEveryMs);

    for (let i = 0; i < maxPolls; i++) {
      await new Promise((r) => setTimeout(r, pollEveryMs));

      const stResp = await fetch(`/api/v2/calibration/strategies/status/${job_id}`);
      if (!stResp.ok) continue;

      const st = await stResp.json();

      const pct = st.progress ?? 0;
      const cw = st.current_window ?? 0;
      const tw = st.total_windows ?? 0;

      const label =
        tw > 0
          ? `WF running... window ${cw}/${tw}`
          : "WF running...";

      setProgress(pct, label);

      if (st.status === "done") {
        setProgress(100, "WF completed ✅");

        // 4) Renderiza resultados usando el MISMO renderer que usabas con /validate
        //    (ojo: el resultado viene dentro de st.result)
        if (!st.result) throw new Error("Job done but no result in status payload");

        renderValidateResponse(st.result); // <-- usa tu función existente de render (plots, tablas, etc.)

        console.log("[calibration_strategies] validateStrategies() DONE ok");
        return;
      }

      if (st.status === "unknown") {
        setProgress(0, "Unknown job (server lost state?)");
        break;
      }
    }

    throw new Error("Timeout waiting for WF job to finish");

  } catch (err) {
    console.error(err);
    // deja un estado visible
    const txt = document.getElementById("wf_progress_text");
    if (txt) txt.textContent = `Error: ${err.message}`;
    console.log("[calibration_strategies] validateStrategies() DONE fail");
  }
}

async function generateReport() {
  console.log("[calibration_strategies] generateReport() START");

  let payload;
  try {
    payload = buildPayload();
  } catch (e) {
    alert(e.message);
    return;
  }

  const res = await fetch("/api/v2/calibration/strategies/report", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });

  const data = await res.json();

  if (data.url) {
    const viewer = document.getElementById("pdf_viewer_section");
    const frame = document.getElementById("pdf_frame");
    frame.src = data.url;
    viewer.classList.remove("d-none");
    viewer.scrollIntoView({ behavior: "smooth" });
  } else {
    alert("Failed to generate report");
  }
}

function wireButtons() {
  document.getElementById("validate_strategies_btn")?.addEventListener("click", validateStrategies);
  document.getElementById("report_strategies_btn")?.addEventListener("click", generateReport);

  document.getElementById("refresh_strategies_btn")?.addEventListener("click", async () => {
    const strategies = await fetchAvailableStrategies();
    renderStrategiesList(strategies);
  });

  document.getElementById("load_step2_btn")?.addEventListener("click", () => {
    loadContextFromLocalStorage();
  });

  document.getElementById("close_pdf_btn")?.addEventListener("click", () => {
    const viewer = document.getElementById("pdf_viewer_section");
    const frame = document.getElementById("pdf_frame");
    frame.src = "";
    viewer.classList.add("d-none");
  });
}

function applyInheritedLock() {
  const locked = document.getElementById("lock_inherited").checked;
  const fields = document.querySelectorAll(".inherited-field");

  fields.forEach(f => {
    f.disabled = locked;
    if (locked) {
      f.classList.add("bg-light");
    } else {
      f.classList.remove("bg-light");
    }
  });
}

async function init() {
  await loadStrategyCatalog();
  addStrategySlot();

  loadContextFromLocalStorage();
  loadFromStep2();
  applyInheritedLock();

  document.getElementById("stop_type")
    .addEventListener("change", updateStopUI);

  wireButtons();
  
  const strategies = await fetchAvailableStrategies();
  renderStrategiesList(strategies);

  // Pre-select 1 strategy by default (moving_average) if exists
  setTimeout(() => {
    const first = document.querySelector('input[type=checkbox][data-strategy-id="moving_average"]');
    if (first) first.checked = true;
  }, 0);
}

document.getElementById("lock_inherited")
  .addEventListener("change", applyInheritedLock);

  
init();
