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

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

let STRATEGY_CATALOG = [];
let strategySlots = [];
let selectedStrategyId = null;
let lastValidationResult = null;
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 (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);
  if (account_equity) setVal("account_equity", account_equity);

  // WF defaults (if stored)
  const wf_train_days = localStorage.getItem("calibration.wf.train_days");
  const wf_test_days = localStorage.getItem("calibration.wf.test_days");
  const wf_step_days = localStorage.getItem("calibration.wf.step_days");
  const wf_min_trades_test = localStorage.getItem("calibration.wf.min_trades_test");

  if (wf_train_days) setVal("wf_train_days", wf_train_days);
  if (wf_test_days) setVal("wf_test_days", wf_test_days);
  if (wf_step_days) setVal("wf_step_days", wf_step_days);
  if (wf_min_trades_test) setVal("wf_min_trades_test", wf_min_trades_test);

  // Optional fees
  const commission = localStorage.getItem("calibration.commission");
  const slippage = localStorage.getItem("calibration.slippage");
  if (commission) setVal("commission", commission);
  if (slippage) setVal("slippage", slippage);
}

function setVal(id, v) {
  const el = document.getElementById(id);
  if (el) el.value = v;
}

function str(id) {
  const el = document.getElementById(id);
  return el ? String(el.value || "").trim() : "";
}

function num(id) {
  const el = document.getElementById(id);
  if (!el) return null;
  const v = parseFloat(el.value);
  return Number.isFinite(v) ? v : null;
}

function sleep(ms) {
  return new Promise(res => setTimeout(res, ms));
}

// =================================================
// FETCH CATALOG
// =================================================

async function fetchCatalog() {
  const symbol = str("symbol");
  const timeframe = str("timeframe");

  const res = await fetch(`/api/v2/calibration/strategies/catalog?symbol=${encodeURIComponent(symbol)}&timeframe=${encodeURIComponent(timeframe)}`);
  if (!res.ok) throw new Error(`catalog failed: ${res.status}`);
  const data = await res.json();

  STRATEGY_CATALOG = data.strategies || [];
  console.log("[catalog] strategies:", STRATEGY_CATALOG.map(s => s.strategy_id));
}

// =================================================
// UI RENDERING
// =================================================

function initSlots() {
  strategySlots = [
    { strategy_id: null, parameters: {} }
  ];
}

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: slotData.parameters || {}
    });

    renderStrategySlot(index);
  });

  // always one empty slot at end (if room)
  if (strategySlots.length < MAX_STRATEGIES) {
    strategySlots.push({ strategy_id: null, parameters: {} });
    renderStrategySlot(strategySlots.length - 1);
  }

  updateCombinationCounter();
}

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="">-- Select --</option>
        ${STRATEGY_CATALOG.map(s => `<option value="${s.strategy_id}">${s.name}</option>`).join("")}
      </select>
    </div>

    <div class="row g-3" id="strategy_params_${index}"></div>

    <div class="d-flex justify-content-between align-items-center mt-3">
      <small class="text-muted">Combinations: <span id="strategy_combo_${index}">0</span></small>
      <button class="btn btn-outline-danger btn-sm" id="remove_strategy_${index}" type="button">
        Remove
      </button>
    </div>
  `;

  container.appendChild(slot);

  const select = document.getElementById(`strategy_select_${index}`);
  const removeBtn = document.getElementById(`remove_strategy_${index}`);

  // set initial value
  select.value = strategySlots[index].strategy_id || "";

  select.addEventListener("change", (e) => {
    onStrategySelected(index, e.target.value);
  });

  removeBtn.addEventListener("click", () => {
    removeStrategySlot(index);
  });

  // if already selected, render params
  if (strategySlots[index].strategy_id) {
    renderParametersOnly(index, strategySlots[index].strategy_id);
  }
}

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 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 || !strategyMeta.parameters_meta) return;

  strategyMeta.parameters_meta.forEach(meta => {

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

    let inputHtml = "";
    const paramName = meta.name;

    // INT / FLOAT
    if (meta.type === "int" || meta.type === "float") {

      inputHtml = `
        <input type="number"
          class="form-control param-input"
          id="${paramName}_value_${index}"
          step="${meta.type === "int" ? "1" : "any"}"
          ${meta.min !== null ? `min="${meta.min}"` : ""}
          ${meta.max !== null ? `max="${meta.max}"` : ""}
          value="${meta.default_value ?? ""}"
        />
      `;
    }

    // ENUM
    else if (meta.type === "enum") {

      const options = (meta.choices || []).map(choice => `
        <option value="${choice}" ${choice === meta.default_value ? "selected" : ""}>
          ${choice}
        </option>
      `).join("");

      inputHtml = `
        <select class="form-select param-input"
          id="${paramName}_value_${index}">
          ${options}
        </select>
      `;
    }

    // BOOL
    else if (meta.type === "bool") {

      inputHtml = `
        <select class="form-select param-input"
          id="${paramName}_value_${index}">
          <option value="true" ${meta.default_value === true ? "selected" : ""}>True</option>
          <option value="false" ${meta.default_value === false ? "selected" : ""}>False</option>
        </select>
      `;
    }

    col.innerHTML = `
      <label class="form-label fw-semibold">${paramName}</label>
      ${inputHtml}
    `;

    paramsContainer.appendChild(col);
  });

  validateParameterInputs();
}

function validateParameterInputs() {

  let valid = true;

  // Limpiar estados previos
  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
    );

    if (!strategyMeta || !strategyMeta.parameters_meta) return;

    strategyMeta.parameters_meta.forEach(meta => {

      const el = document.getElementById(`${meta.name}_value_${index}`);
      if (!el) return;

      let raw = el.value;
      let value = raw;

      // 🔹 INT
      if (meta.type === "int") {
        value = parseInt(raw);
        if (isNaN(value)) valid = false;
      }

      // 🔹 FLOAT
      else if (meta.type === "float") {
        value = parseFloat(raw);
        if (isNaN(value)) valid = false;
      }

      // 🔹 BOOL
      else if (meta.type === "bool") {
        if (raw !== "true" && raw !== "false") valid = false;
      }

      // 🔹 ENUM
      else if (meta.type === "enum") {
        if (!meta.choices || !meta.choices.includes(raw)) {
          valid = false;
        }
      }

      // 🔹 Rango (solo números)
      if ((meta.type === "int" || meta.type === "float") && !isNaN(value)) {

        if (meta.min !== null && meta.min !== undefined && value < meta.min) {
          valid = false;
        }

        if (meta.max !== null && meta.max !== undefined && value > meta.max) {
          valid = false;
        }
      }

      if (!valid) {
        el.classList.add("is-invalid");
      }

    });

  });

  updateCombinationCounter();

  return valid;
}

function updateCombinationCounter() {

  let hasAnyStrategy = false;

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

    if (!slot.strategy_id) return;

    hasAnyStrategy = true;

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

  const globalTotal = hasAnyStrategy ? 1 : 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();
}

// =================================================
// PAYLOAD
// =================================================

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 wf_min_trades_test = num("wf_min_trades_test")

  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,
      min_trades_test: wf_min_trades_test,
    },
    commission: num("commission") ?? 0.001,
    slippage: num("slippage") ?? 0.0005,
  };
}

function collectSelectedStrategies() {

  const strategies = [];

  console.log("strategySlots:", strategySlots);

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

    if (!slot.strategy_id) return;

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

    if (!strategyMeta) return;

    const parameters = {};

    strategyMeta.parameters_meta.forEach(meta => {

      const el = document.getElementById(`${meta.name}_value_${index}`);
      if (!el) return;

      let value = el.value;

      if (meta.type === "int") {
        value = parseInt(value);
      }
      else if (meta.type === "float") {
        value = parseFloat(value);
      }
      else if (meta.type === "bool") {
        value = value === "true";
      }

      parameters[meta.name] = value;
    });


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

  });

  console.log("Collected strategies:", strategies);

  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);
}

// =================================================
// 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);
}


// =================================================
// DIFFERENT RENDER PLOTS
// =================================================

function renderEquityAndReturns(strategyId, s, data) {
  const equity = s.window_equity || [];
  const ret = s.window_returns_pct || [];
  const trd = s.window_trades || [];

  const regimeWindows = s.window_regimes || s.diagnostics?.regimes?.by_window || [];

  function regimeBgColor(regime) {
    switch (regime) {
      case "bull_strong":
        return "rgba(0,114,178,0.20)";
      case "bull_moderate":
        return "rgba(0,114,178,0.10)";
      case "bear_strong":
        return "rgba(230,159,0,0.20)";
      case "bear_moderate":
        return "rgba(230,159,0,0.10)";
      default:
        return "rgba(150,150,150,0.12)";
    }
  }

  function buildRegimeBackgroundShapes(regimeWindows, topYRef = "paper") {
    if (!Array.isArray(regimeWindows) || regimeWindows.length === 0) return [];

    const shapes = [];

    regimeWindows.forEach((w, idx) => {
      const x0 = idx - 0.5;
      const x1 = idx + 0.5;

      shapes.push({
        type: "rect",
        xref: "x",
        yref: topYRef,
        x0,
        x1,
        y0: 0.60,   // mismo inicio que domain del gráfico superior
        y1: 1.00,   // mismo final que domain del gráfico superior
        fillcolor: regimeBgColor(w.regime_detail || w.regime),
        line: { width: 0 },
        layer: "below"
      });
    });

    return shapes;
  }

  // X común (windows)
  const n = Math.max(equity.length, ret.length, trd.length);
  const x = Array.from({ length: n }, (_, i) => i);

  // ---- Escalado para alinear visualmente el 0 (returns) con el 0 (trades) ----
  const retMax = Math.max(0, ...ret);
  const retMin = Math.min(0, ...ret);

  const minTrades = data.config?.wf?.min_trades_test ?? 10;

  const trdMaxRaw = Math.max(0, ...trd);
  const trdMax = Math.max(trdMaxRaw, minTrades);

  const retPosSpan = Math.max(1e-9, retMax);
  const retNegSpan = Math.abs(retMin);

  const trdNegSpan = (retNegSpan / retPosSpan) * trdMax;

  const y1Range = [retMin, retMax];
  const y2Range = [-trdNegSpan, trdMax];

  // ---- Trazas ----
  const equityTrace = {
    x,
    y: equity,
    type: "scatter",
    mode: "lines",
    name: "Equity",
    xaxis: "x",
    yaxis: "y"
  };

  const returnsTrace = {
    x,
    y: ret,
    type: "bar",
    name: "Return %",
    marker: { color: "#3b82f6" },
    xaxis: "x2",
    yaxis: "y2",
    offsetgroup: "returns",
    alignmentgroup: "bottom"
  };

  const tradesTrace = {
    x,
    y: trd,
    type: "bar",
    name: "Trades",
    marker: { color: "#f59e0b" },
    xaxis: "x2",
    yaxis: "y3",
    offsetgroup: "trades",
    alignmentgroup: "bottom"
  };

  const regimeShapesTop = buildRegimeBackgroundShapes(regimeWindows);

  // ---- Layout ----
  const layout = {
    grid: { rows: 2, columns: 1, pattern: "independent" },

    // IMPORTANTE: share X de verdad (pan/zoom sincronizado)
    xaxis: { matches: "x2", showgrid: true },
    xaxis2: { matches: "x", showgrid: true },

    // Dominios (más altura para ver mejor)
    // Ajusta estos números a gusto:
    yaxis:  { 
      domain: [0.60, 1.00], 
      title: "Equity", 
      showgrid: true, 
      rangemode: "tozero" 
    },
    xaxis:  { 
      domain: [0.00, 1.00],
      anchor: "y" 
    },

    yaxis2: {
      domain: [0.00, 0.25],
      title: "Return %",
      range: y1Range,
      zeroline: true,
      zerolinewidth: 2,
      showgrid: true
    },
    xaxis2: {
      domain: [0.00, 1.00],
      anchor: "y2",
      title: "Windows"
    },

    yaxis3: {
      title: "Trades",
      overlaying: "y2",
      side: "right",
      range: y2Range,
      zeroline: true,
      zerolinewidth: 2,
      showgrid: false
    },

    // Barras lado a lado
    barmode: "group",
    bargap: 0.2,
    
    shapes: [
      ...regimeShapesTop,
      {
        type: "line",
        x0: -0.5,
        x1: n - 0.5,
        y0: minTrades,
        y1: minTrades,
        xref: "x2",
        yref: "y3",
        line: { color: "red", width: 2, dash: "dash" }
      }
    ],

    legend: { orientation: "h" },
    margin: { t: 50, r: 70, l: 70, b: 50 }
  };

  const topDomain = layout.yaxis.domain;   // [start,end]
  const botDomain = layout.yaxis2.domain;  // [start,end]

  layout.annotations = [
    {
      text: `Equity — ${strategyId}`,
      x: 0.5, xref: "paper",
      y: topDomain[1] + 0.05, yref: "paper",
      showarrow: false,
      font: { size: 16 }
    },
    {
      text: `Returns & Trades — ${strategyId}`,
      x: 0.5, xref: "paper",
      y: botDomain[1] + 0.05, yref: "paper",
      showarrow: false,
      font: { size: 16 }
    }
  ];

  Plotly.newPlot("plot_strategy", [equityTrace, returnsTrace, tradesTrace], layout, {
    displayModeBar: true,
    responsive: true
  });

  const gd = document.getElementById("plot_strategy");

  // Limpia listeners anteriores (si re-renderizas)
  gd.removeAllListeners?.("plotly_relayout");

  // Flag para evitar bucles cuando hacemos relayout desde el listener
  gd.__syncing = false;

  function clamp01(v) {
    return Math.max(0, Math.min(1, v));
  }

  function zeroFrac(min, max) {
    const span = max - min;
    if (!isFinite(span) || span <= 1e-12) return 0;
    return clamp01((0 - min) / span);
  }

  // Dado un max fijo y una fracción f (posición del 0), calcula el min
  function minFromMaxAndFrac(max, f) {
    const denom = Math.max(1e-9, (1 - f));
    return -(f / denom) * max;
  }

  gd.on("plotly_relayout", (ev) => {
    if (gd.__syncing) return;

    const update = {};

    // ===== 0) DETECTAR AUTOSCALE / RESET (prioridad máxima) =====
    // Plotly puede indicar autoscale de varias maneras.
    const autoscaleTriggered =
      ev["yaxis2.autorange"] === true ||
      ev["yaxis3.autorange"] === true ||
      ev["yaxis.autorange"] === true ||
      ev["xaxis.autorange"] === true ||
      ev["xaxis2.autorange"] === true ||
      ev["autosize"] === true ||
      ev["resetScale2d"] === true ||
      ev["xaxis.range[0]"] === undefined && ev["xaxis.range[1]"] === undefined && ev["yaxis.range[0]"] === undefined && ev["yaxis.range[1]"] === undefined
        ? false
        : false;

    // Si el usuario pulsa Autoscale/Reset, queremos SIEMPRE:
    // - Reforzar yaxis2/yaxis3 con tus rangos calculados (0 alineado)
    // - Volver a dibujar la línea minTrades
    // - (Opcional) dejar X en autorange en ambos
    if (
      ev["yaxis2.autorange"] === true ||
      ev["yaxis3.autorange"] === true ||
      ev["yaxis.autorange"] === true ||
      ev["xaxis.autorange"] === true ||
      ev["xaxis2.autorange"] === true ||
      ev["resetScale2d"] === true
    ) {
      // update["yaxis2.autorange"] = false;
      // update["yaxis3.autorange"] = false;
      update["yaxis2.range"] = y1Range;
      update["yaxis3.range"] = y2Range;

      // Asegura que la línea de Min Trades se vea SIEMPRE
      update["shapes[0].type"] = "line";
      update["shapes[0].xref"] = "x2";
      update["shapes[0].yref"] = "y3";
      update["shapes[0].x0"] = -0.5;
      update["shapes[0].x1"] = n - 0.5;
      update["shapes[0].y0"] = minTrades;
      update["shapes[0].y1"] = minTrades;
      update["shapes[0].line.color"] = "red";
      update["shapes[0].line.width"] = 2;
      update["shapes[0].line.dash"] = "dash";

      // Mantén X sincronizado también tras autoscale
      // if (ev["xaxis.autorange"] === true || ev["xaxis2.autorange"] === true || ev["resetScale2d"] === true) {
      //   update["xaxis.autorange"] = true;
      //   update["xaxis2.autorange"] = true;
      // }

      gd.__syncing = true;
      Plotly.relayout(gd, update).finally(() => {
        gd.__syncing = false;
      });
      return; // IMPORTANTÍSIMO: no seguimos con el resto de sincronizaciones
    }

    // ===== 1) Sync X arriba/abajo (pan/zoom normal) =====
    if (ev["xaxis2.range[0]"] !== undefined && ev["xaxis2.range[1]"] !== undefined) {
      update["xaxis.range"] = [ev["xaxis2.range[0]"], ev["xaxis2.range[1]"]];
      update["xaxis.autorange"] = false;
    }

    if (ev["xaxis.range[0]"] !== undefined && ev["xaxis.range[1]"] !== undefined) {
      update["xaxis2.range"] = [ev["xaxis.range[0]"], ev["xaxis.range[1]"]];
      update["xaxis2.autorange"] = false;
    }

    // ===== 2) Mantener 0 alineado cuando pan/zoom en Y2 o Y3 =====
    const y2Changed = ev["yaxis2.range[0]"] !== undefined && ev["yaxis2.range[1]"] !== undefined;
    const y3Changed = ev["yaxis3.range[0]"] !== undefined && ev["yaxis3.range[1]"] !== undefined;

    const full = gd._fullLayout || {};
    const curY2 = full.yaxis2?.range || y1Range;
    const curY3 = full.yaxis3?.range || y2Range;

    if (y2Changed && !y3Changed) {
      const newY2 = [ev["yaxis2.range[0]"], ev["yaxis2.range[1]"]];
      const f = zeroFrac(newY2[0], newY2[1]);

      const y3MaxKeep = (curY3 && curY3.length === 2) ? curY3[1] : y2Range[1];
      const y3Min = minFromMaxAndFrac(y3MaxKeep, f);

      update["yaxis2.autorange"] = false;
      update["yaxis3.autorange"] = false;
      update["yaxis2.range"] = newY2;
      update["yaxis3.range"] = [y3Min, y3MaxKeep];
    }

    if (y3Changed && !y2Changed) {
      const newY3 = [ev["yaxis3.range[0]"], ev["yaxis3.range[1]"]];
      const f = zeroFrac(newY3[0], newY3[1]);

      const y2MaxKeep = (curY2 && curY2.length === 2) ? curY2[1] : y1Range[1];
      const y2Min = minFromMaxAndFrac(y2MaxKeep, f);

      update["yaxis2.autorange"] = false;
      update["yaxis3.autorange"] = false;
      update["yaxis3.range"] = newY3;
      update["yaxis2.range"] = [y2Min, y2MaxKeep];
    }

    // Si ambos cambian (zoom box), prioriza el zeroFrac de y2
    if (y2Changed && y3Changed) {
      const newY2 = [ev["yaxis2.range[0]"], ev["yaxis2.range[1]"]];
      const f = zeroFrac(newY2[0], newY2[1]);

      const newY3 = [ev["yaxis3.range[0]"], ev["yaxis3.range[1]"]];
      const y3MaxKeep = newY3[1];
      const y3Min = minFromMaxAndFrac(y3MaxKeep, f);

      update["yaxis2.autorange"] = false;
      update["yaxis3.autorange"] = false;
      update["yaxis2.range"] = newY2;
      update["yaxis3.range"] = [y3Min, y3MaxKeep];
    }

    // ===== 3) Re-asegurar la línea minTrades (por si Plotly la toca en relayouts) =====
    // (No molesta y evita que desaparezca en algunos reset)
    update["shapes[0].y0"] = minTrades;
    update["shapes[0].y1"] = minTrades;

    if (Object.keys(update).length === 0) return;

    gd.__syncing = true;
    Plotly.relayout(gd, update).finally(() => {
      gd.__syncing = false;
    });
  });
}

function renderRollingSharpe(strategyId, s, data) {
  const roll = s.diagnostics?.rolling?.rolling_sharpe_like || [];
  const x = roll.map((_, i) => i + 1);

  const k = s.diagnostics?.rolling?.rolling_window ?? "?";

  Plotly.newPlot(
    "plot_strategy",
    [
      {
        x,
        y: roll,
        type: "scatter",
        mode: "lines+markers",
        name: `Rolling Sharpe-like (k=${k})`
      }
    ],
    {
      margin: { t: 60, r: 40, l: 60, b: 50 },
      title: { text: `Rolling Sharpe-like — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
      xaxis: { title: "Window", showgrid: true, zeroline: false },
      yaxis: { title: "Sharpe-like", showgrid: true, zeroline: true, zerolinewidth: 2 },
      legend: { orientation: "h" }
    },
    { responsive: true, displayModeBar: true }
  );
}

function renderOOSReturnsDistribution(strategyId, s, data) {
  const edges = s.diagnostics?.distribution?.hist_bin_edges || [];
  const counts = s.diagnostics?.distribution?.hist_counts || [];

  const centers = edges.map((_, i) => (edges[i] + edges[i + 1]) / 2);

  Plotly.newPlot("plot_strategy", [{
    x: centers,
    y: counts,
    type: "bar",
    name: "OOS Return% (bins)"
  }], {
    margin: { t: 40 },
    title: { text: `OOS Returns Distribution — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
    xaxis: { title: "Return % (window)" },
    yaxis: { title: "Count" }
  });
}

function renderDrawdownEvolution(strategyId, s, data) {
  const dd = s.diagnostics?.drawdown?.drawdown_pct || [];
  const x = dd.map((_, i) => i);

  Plotly.newPlot("plot_strategy", [{
    x,
    y: dd,
    type: "scatter",
    mode: "lines",
    name: "Drawdown %"
  }], {
    margin: { t: 40 },
    title: { text: `Drawdown Evolution — ${strategyId}`, x: 0.5 }, // ✅ centrado + dinámico
    xaxis: { title: "Point (initial + windows)" },
    yaxis: { title: "Drawdown %", zeroline: true }
  });
}

function renderTradeDensity(strategyId, s, data) {
  const tpw = s.diagnostics?.trades?.trades_per_window || [];
  const tpd = s.diagnostics?.trades?.trades_per_day || [];
  const x = tpw.map((_, i) => i + 1);

  const tpwMax = Math.max(0, ...tpw);
  const tpdMax = Math.max(0, ...tpd);

  Plotly.newPlot("plot_strategy", [
    {
      x,
      y: tpw,
      type: "bar",
      name: "Trades / window",
      yaxis: "y"
    },
    {
      x,
      y: tpd,
      type: "scatter",
      mode: "lines+markers",
      name: "Trades / day",
      yaxis: "y2"
    }
  ], {
    margin: { t: 40 },
    title: { text: `Trade Density — ${strategyId}`, x: 0.5 },
    barmode: "group",
    xaxis: { title: "Window" },

    yaxis: {
      title: "Trades / window",
      range: [0, tpwMax],
      zeroline: true,
      zerolinewidth: 2
    },

    yaxis2: {
      title: "Trades / day",
      overlaying: "y",
      side: "right",
      range: [0, tpdMax],
      zeroline: true,
      zerolinewidth: 2
    }
  });
}

// =================================================
// 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 || ""}
          ${Array.isArray(r.warnings) && r.warnings.length ? `<div class="mt-1"><small>${r.warnings.map(escapeHtml).join(" · ")}</small></div>` : ""}
        </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 = "";

  // ✅ usar results, no series (para que aparezcan también warning/fail)
  const ids = (data.results || []).map(r => r.strategy_id);

  ids.forEach((sid) => {
    const opt = document.createElement("option");
    opt.value = sid;
    opt.textContent = sid;
    sel.appendChild(opt);
  });

  sel.onchange = () => {
    const sid = sel.value;
    selectStrategy(sid, data);
  };

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

function selectStrategy(strategyId, data) {
  if (!strategyId || !data) return;

  // Actualiza selectedStrategyId
  selectedStrategyId = strategyId;

  const row = (data.results || []).find(r => r.strategy_id === strategyId);

  // 1) Alerts por status (siempre explícito)
  if (row?.status === "warning") {
    showPlotAlert("warning", `WARNING — ${strategyId}`, row.message || "Strategy warning.", row.warnings);
  } else if (row?.status === "fail") {
    showPlotAlert("danger", `FAIL — ${strategyId}`, row.message || "Strategy failed.", row.warnings);
  } else {
    clearPlotAlert();
  }

  // 2) Si el backend indica que NO hay serie, no intentamos renderizar
  if (row && row.series_available === false) {
    showPlotAlert(
      row.status === "fail" ? "danger" : "warning",
      `${(row.status || "warning").toUpperCase()} — ${strategyId}`,
      row.series_error || row.message || "No chart series available for this strategy.",
      row.warnings
    );
    clearPlots();
    highlightSelectedRow(strategyId);
    return;
  }

  // 3) Verificar si los datos de la estrategia están disponibles
  const strategyData = data?.series?.strategies?.[selectedStrategyId];

  if (!strategyData) {
    showPlotAlert(
      "warning",
      `No data available — ${strategyId}`,
      "Strategy data not available for rendering.",
      []
    );
    clearPlots();
    highlightSelectedRow(strategyId);
    return;
  }

  // 4) Mantén el gráfico previamente seleccionado en el dropdown
  const chartType = document.getElementById("plot_strategy_select").value;
  renderChart(chartType, selectedStrategyId, strategyData, data);  // Renderiza el gráfico correctamente
  renderRegimeSummary(selectedStrategyId, strategyData, data);

  highlightSelectedRow(strategyId);

  // scroll automático al gráfico
  document.getElementById("plot_strategy")?.scrollIntoView({
    behavior: "smooth",
    block: "start"
  });
}

function renderValidateResponse(data) {

  lastValidationResult = 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.results && data.results.length > 0) {
    if (!selectedStrategyId) {
      selectedStrategyId = data.results[0].strategy_id;
    }
    selectStrategy(selectedStrategyId, data);
  }

  // -------------------------------
  // 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>
          <th>Best Regime</th>
        </tr>
      </thead>
      <tbody>`;

    for (const r of data.results) {
      
      let bestRegime = "-";

      const strat = data?.series?.strategies?.[r.strategy_id];
      const perf = strat?.diagnostics?.regimes?.performance;

      if (perf) {
        const entries = ["bull", "sideways", "bear"]
          .map(k => ({
            regime: k,
            val: perf?.[k]?.mean_return_pct ?? -Infinity
          }));

        entries.sort((a,b)=>b.val-a.val);

        if (entries[0].val > -Infinity) {
          bestRegime = entries[0].regime;
        }
      }

      html += `
        <tr>
          <td class="strategy-row"
            data-strategy="${r.strategy_id}"
            style="cursor:pointer; font-weight:600;">
            ${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>
          <td>${bestRegime}</td>
        </tr>
      `;

      // 🔸 Mostrar warnings debajo de la fila si existen
      if (r.warnings && r.warnings.length > 0) {

        html += `
          <tr class="table-warning">
            <td colspan="6">
              <ul class="mb-0">
                ${r.warnings.map(w => `<li>${w}</li>`).join("")}
              </ul>
            </td>
          </tr>
        `;
      }
    }

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

    document.querySelectorAll(".strategy-row").forEach(el => {
      el.addEventListener("click", function () {

        console.log("Clicked:", selectedStrategyId);

        selectedStrategyId = this.dataset.strategy;
        selectStrategy(selectedStrategyId, lastValidationResult);
      });
    });
  }
}

function renderChart(chartType, strategyId, s, data) {
  switch (chartType) {
    case "equity":
      renderEquityAndReturns(strategyId, s, data);
      break;
    case "rolling_sharpe":
      renderRollingSharpe(strategyId, s, data);
      break;
    case "hist_oos_returns":
      renderOOSReturnsDistribution(strategyId, s, data);
      break;
    case "drawdown":
      renderDrawdownEvolution(strategyId, s, data);
      break;
    case "trade_density":
      renderTradeDensity(strategyId, s, data);
      break;
    default:
      renderEquityAndReturns(strategyId, s, data);
      break;
  }
}

function highlightSelectedRow(strategyId) {

  document.querySelectorAll(".strategy-row").forEach(el => {
    el.style.backgroundColor = "";
  });

  const active = document.querySelector(
    `.strategy-row[data-strategy="${strategyId}"]`
  );

  if (active) {
    active.style.backgroundColor = "#e6f0ff";
  }
}

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();

  document.getElementById("plot_strategy_select").addEventListener("change", function() {
    const chartType = this.value;
    const strategyData = lastValidationResult.series.strategies[selectedStrategyId];

    // Verifica que selectedStrategyId tenga el valor correcto
    console.log("selectedStrategyId:", selectedStrategyId);
    console.log("Strategy Data:", strategyData);

    renderChart(chartType, selectedStrategyId, strategyData, lastValidationResult);
  });
  
  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);
}

// =================================================
// MARKET REGIME
// =================================================

function fmtPct(v, digits = 2) {
  const n = Number(v ?? 0);
  return `${n.toFixed(digits)}%`;
}

function fmtNum(v, digits = 2) {
  const n = Number(v ?? 0);
  return n.toFixed(digits);
}

function regimeBadgeClass(regime) {
  switch (regime) {
    case "bull":
    case "bull_moderate":
    case "bull_strong":
      return "bg-blue-lt text-blue";
    case "bear":
    case "bear_moderate":
    case "bear_strong":
      return "bg-orange-lt text-orange";
    default:
      return "bg-secondary-lt text-secondary";
  }
}

function regimeLabel(regime) {
  switch (regime) {
    case "bull_strong": return "Bull strong";
    case "bull_moderate": return "Bull moderate";
    case "bear_strong": return "Bear strong";
    case "bear_moderate": return "Bear moderate";
    case "bull": return "Bull";
    case "bear": return "Bear";
    default: return "Sideways";
  }
}

function ensureRegimeContainer() {
  let el = document.getElementById("regime_analysis_wrap");
  if (el) return el;

  const anchor = document.getElementById("plot_strategy");
  if (!anchor || !anchor.parentElement) return null;

  el = document.createElement("div");
  el.id = "regime_analysis_wrap";
  el.className = "mt-4";

  anchor.parentElement.appendChild(el);
  return el;
}

function clearRegimeSummary() {
  const el = document.getElementById("regime_analysis_wrap");
  if (el) el.innerHTML = "";
}

function getBestRegime(perf) {
  const candidates = ["bull", "sideways", "bear"]
    .map((k) => ({
      regime: k,
      n_windows: Number(perf?.[k]?.n_windows ?? 0),
      mean_return_pct: Number(perf?.[k]?.mean_return_pct ?? -Infinity),
    }))
    .filter((x) => x.n_windows > 0);

  if (!candidates.length) return null;

  candidates.sort((a, b) => b.mean_return_pct - a.mean_return_pct);
  return candidates[0].regime;
}

function renderRegimeSummary(strategyId, s, data) {
  const wrap = ensureRegimeContainer();
  if (!wrap) return;

  const regimesDiag = s?.diagnostics?.regimes || {};
  const perf = regimesDiag.performance?.group || {};
  const perfDetail = regimesDiag.performance?.detail || {};
  const byWindow = s?.window_regimes || regimesDiag.by_window || [];
  const cfg = regimesDiag.config || data?.regimes?.config || {};

  const bestRegime = getBestRegime(perf);

  const detailOrderTop = ["bull_moderate", "bear_moderate"];
  const detailOrderBottom = ["bull_strong", "sideways", "bear_strong"];

  function renderDetailCard(regime, extraClass = "") {
    const p = perfDetail?.[regime] || {};
    return `
      <div class="${extraClass}">
        <div class="card h-100">
          <div class="card-body">
            <div class="d-flex justify-content-between align-items-center mb-2">
              <h4 class="card-title mb-0">${regimeLabel(regime)}</h4>
              <span class="badge ${regimeBadgeClass(regime)}">${regimeLabel(regime).toUpperCase()}</span>
            </div>

            <div class="row g-2 small">
              <div class="col-6">
                <div class="text-secondary">Windows</div>
                <div class="fw-semibold">${Number(p.n_windows ?? 0)}</div>
              </div>
              <div class="col-6">
                <div class="text-secondary">Mean return</div>
                <div class="fw-semibold">${fmtPct(p.mean_return_pct)}</div>
              </div>
              <div class="col-6">
                <div class="text-secondary">Positive rate</div>
                <div class="fw-semibold">${fmtPct((Number(p.positive_window_rate ?? 0) * 100.0))}</div>
              </div>
              <div class="col-6">
                <div class="text-secondary">Avg trades</div>
                <div class="fw-semibold">${fmtNum(p.avg_trades)}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    `;
  }

  const topCards = detailOrderTop
    .map((regime) => renderDetailCard(regime, "col-md-6"))
    .join("");

  const bottomCards = detailOrderBottom
    .map((regime) => renderDetailCard(regime, "col-md-4"))
    .join("");

  const rows = byWindow.map((w) => `
    <tr>
      <td>${Number(w.window ?? 0)}</td>
      <td>
        <span class="badge ${regimeBadgeClass(w.regime_detail || w.regime)}">
          ${regimeLabel(w.regime_detail || w.regime)}
        </span>
      </td>
      <td>${fmtPct(Number(w.bull_strong_pct ?? 0) * 100.0)}</td>
      <td>${fmtPct(Number(w.bull_moderate_pct ?? 0) * 100.0)}</td>
      <td>${fmtPct(Number(w.sideways_detail_pct ?? 0) * 100.0)}</td>
      <td>${fmtPct(Number(w.bear_moderate_pct ?? 0) * 100.0)}</td>
      <td>${fmtPct(Number(w.bear_strong_pct ?? 0) * 100.0)}</td>
    </tr>
  `).join("");

  wrap.innerHTML = `
    <div class="card">
      <div class="card-header d-flex justify-content-between align-items-center">
        <div>
          <h3 class="card-title mb-0">Regime Analysis — ${strategyId}</h3>
          <div class="text-secondary small mt-1">
            EMA: ${(cfg.ema_periods || []).join(" / ")}
            · Bull persistence: ${cfg.bull_persistence_bars ?? "-"}
            · Sideways persistence: ${cfg.sideways_persistence_bars ?? "-"}
            · Bear persistence: ${cfg.bear_persistence_bars ?? "-"}
            · Bull ≥ ${cfg.bull_threshold ?? "-"}
            · Bear ≤ ${cfg.bear_threshold ?? "-"}
          </div>
        </div>
        ${
          bestRegime
            ? `<span class="badge ${regimeBadgeClass(bestRegime)}">
                 Best regime: ${regimeLabel(bestRegime)}
               </span>`
            : `<span class="badge bg-secondary-lt text-secondary">Best regime: —</span>`
        }
      </div>

      <div class="card-body">
        <div class="row justify-content-center g-3 mb-3">
          <div class="col-12 col-xl-10">
            <div class="row g-3">
              ${topCards}
            </div>
          </div>
        </div>

        <div class="row justify-content-center g-3 mb-4">
          <div class="col-12">
            <div class="row g-3">
              ${bottomCards}
            </div>
          </div>
        </div>

        <div class="table-responsive">
          <table class="table table-sm table-vcenter">
            <thead>
              <tr>
                <th>Window</th>
                <th>Majority regime</th>
                <th>Bull strong %</th>
                <th>Bull mod %</th>
                <th>Sideways %</th>
                <th>Bear mod %</th>
                <th>Bear strong %</th>
              </tr>
            </thead>
            <tbody>
              ${rows || `<tr><td colspan="7" class="text-secondary">No regime data available.</td></tr>`}
            </tbody>
          </table>
        </div>
      </div>
    </div>
  `;
}

// =================================================
// PLOT ALERTS (Tabler) + SAFE PLOT CLEAR
// =================================================

function ensurePlotAlertContainer() {
  // Lo colocamos antes del primer plot si existe
  let el = document.getElementById("plot_alert");
  if (el) return el;

  const anchor = document.getElementById("plot_strategy");
  if (!anchor || !anchor.parentElement) return null;

  el = document.createElement("div");
  el.id = "plot_alert";
  el.className = "mb-3";
  anchor.parentElement.insertBefore(el, anchor);
  return el;
}

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

function showPlotAlert(type, title, message, warnings) {
  const el = ensurePlotAlertContainer();
  if (!el) return;

  const warnHtml = Array.isArray(warnings) && warnings.length
    ? `<ul class="mb-0">${warnings.map(w => `<li>${escapeHtml(w)}</li>`).join("")}</ul>`
    : "";

  el.innerHTML = `
    <div class="alert alert-${type}" role="alert">
      <div>
        <h4 class="alert-title">${escapeHtml(title)}</h4>
        <div class="text-secondary">${escapeHtml(message || "")}</div>
        ${warnHtml}
      </div>
    </div>
  `;
}

function clearPlotAlert() {
  const el = document.getElementById("plot_alert");
  if (el) el.innerHTML = "";
}

function clearPlots() {
  const eq = document.getElementById("plot_strategy");
  if (eq) eq.innerHTML = "";

  clearRegimeSummary();
}

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

init();
