(function () {
  "use strict";

  var state = {
    status: null,
    busy: false,
    chartSymbol: "",
  };

  function byId(id) {
    return document.getElementById(id);
  }

  function text(id, value) {
    var element = byId(id);
    if (element) {
      element.textContent = value;
    }
  }

  function html(id, value) {
    var element = byId(id);
    if (element) {
      element.innerHTML = value;
    }
  }

  function addClass(element, className, enabled) {
    if (!element) {
      return;
    }
    if (enabled) {
      element.classList.add(className);
    } else {
      element.classList.remove(className);
    }
  }

  var base = window.location.pathname.indexOf("/phenocrypto") !== -1 ? "/phenocrypto" : "";

  function api(path, options) {
    options = options || {};
    var headers = options.headers || {};
    headers["Content-Type"] = "application/json";
    options.headers = headers;
    options.credentials = "same-origin";

    return fetch(base + path, options).then(function (response) {
      return response.json().then(function (payload) {
        if (response.status === 401) {
          window.location.href = base + "/login";
          throw new Error("Login required");
        }
        if (!response.ok || payload.ok === false) {
          throw new Error(payload.error || "Request failed with " + response.status);
        }
        return payload;
      });
    });
  }

  function setBusy(value) {
    state.busy = value;
    ["runAllBtn", "startLoopBtn", "stopLoopBtn", "reloadBtn"].forEach(function (id) {
      var button = byId(id);
      if (button) {
        button.disabled = value;
      }
    });
    Array.prototype.forEach.call(document.querySelectorAll(".run-pair"), function (button) {
      button.disabled = value;
    });
  }

  function toast(message) {
    var element = byId("toast");
    if (!element) {
      return;
    }
    element.textContent = message;
    element.classList.add("show");
    window.clearTimeout(toast.timer);
    toast.timer = window.setTimeout(function () {
      element.classList.remove("show");
    }, 3200);
  }

  function strategyLabel(value) {
    return String(value || "").split("_").join(" ");
  }

  function money(value, currency) {
    return Number(value || 0).toFixed(2) + " " + currency;
  }

  function formatPrice(value) {
    var number = Number(value || 0);
    if (!number) {
      return "--";
    }
    if (number >= 1000) {
      return number.toLocaleString(undefined, { maximumFractionDigits: 2 });
    }
    if (number >= 1) {
      return number.toLocaleString(undefined, { maximumFractionDigits: 4 });
    }
    return number.toLocaleString(undefined, { maximumFractionDigits: 8 });
  }

  function lastResultFor(symbol) {
    var history = state.status && state.status.history ? state.status.history : [];
    for (var i = history.length - 1; i >= 0; i -= 1) {
      var results = history[i].results || [];
      for (var j = 0; j < results.length; j += 1) {
        if (results[j].symbol === symbol) {
          return results[j];
        }
      }
    }
    return null;
  }

  function renderStatus(status) {
    state.status = status;
    var pairs = status.pairs || [];
    var history = status.history || [];
    var loop = status.loop || { running: false, sleep_seconds: 60 };
    var credentials = status.credentials || {};
    var modeBadge = byId("modeBadge");
    var loopBadge = byId("loopBadge");
    var apiStatus = byId("apiStatus");
    var hasKeys = credentials.has_api_key && credentials.has_api_secret;
    var lastBatch = history.length ? history[history.length - 1] : null;

    text("modeBadge", status.dry_run ? "Dry run" : "Live trading");
    addClass(modeBadge, "warn", !status.dry_run);
    text("loopBadge", loop.running ? "Loop every " + loop.sleep_seconds + "s" : "Loop off");
    addClass(loopBadge, "muted", !loop.running);
    text("pairCount", pairs.length);
    text("orderSize", money(status.fixed_order_quote, status.quote_currency));
    text("cooldown", String(status.cooldown_seconds) + "s");
    text("lastUpdate", lastBatch ? lastBatch.iso_time : "--");
    text("configPath", status.config_path || "");
    text("apiStatus", hasKeys ? "Saved" : "Missing");
    if (apiStatus) {
      apiStatus.className = hasKeys ? "" : "warn-text";
    }
    text("apiStorage", credentials.storage || "");

    renderSymbolStrip(pairs);
    renderBotStatus(status.bot_status || {});
    renderChartSymbolOptions(pairs);
    renderPairs(pairs);
    renderActivity(history);
  }

  function renderBotStatus(botStatus) {
    var mode = botStatus.mode === "live" ? "Live trading" : "Dry run / paper trading";
    var service = botStatus.service === "running" ? "Running" : "Not running";
    var loop = botStatus.loop_running ? "Running" : "Loop off";
    var openPositions = botStatus.open_positions || {};
    var openSymbols = Object.keys(openPositions);

    text("botMode", mode);
    text("serviceStatus", service);
    text("loopStatus", loop);
    text("lastEntryStatus", formatTradeEvent(botStatus.last_entry, "No entry yet"));
    text("lastCloseStatus", formatTradeEvent(botStatus.last_close, "No close yet"));
    text("openPositionStatus", String(botStatus.open_position_count || openSymbols.length || 0));
    renderServiceButtons(botStatus.service === "running");

    if (!byId("openPositionList")) {
      return;
    }
    if (!openSymbols.length) {
      html("openPositionList", '<span class="marker-chip">No open positions</span>');
      return;
    }
    html(
      "openPositionList",
      openSymbols
        .map(function (symbol) {
          var position = openPositions[symbol] || {};
          return (
            '<span class="marker-chip entry">' +
            symbol +
            " entry " +
            formatPrice(position.entry_price) +
            " " +
            (position.mode || "") +
            "</span>"
          );
        })
        .join("")
    );
  }

  function renderServiceButtons(isRunning) {
    var startButton = byId("startServiceBtn");
    var stopButton = byId("stopServiceBtn");
    if (startButton) {
      startButton.disabled = isRunning;
      addClass(startButton, "enabled", !isRunning);
      startButton.textContent = isRunning ? "Bot service running" : "Start bot service";
    }
    if (stopButton) {
      stopButton.disabled = !isRunning;
      addClass(stopButton, "enabled", isRunning);
      stopButton.textContent = isRunning ? "Stop bot service" : "Bot service stopped";
    }
  }

  function formatTradeEvent(event, fallback) {
    if (!event) {
      return fallback;
    }
    var label = event.symbol + " " + formatPrice(event.price);
    if (event.iso_time) {
      label += " " + event.iso_time;
    }
    return label;
  }

  function renderChartSymbolOptions(pairs) {
    var select = byId("chartSymbol");
    if (!select || !pairs.length) {
      return;
    }
    var current = select.value || state.chartSymbol || pairs[0].symbol;
    select.innerHTML = pairs
      .map(function (pair) {
        var selected = pair.symbol === current ? " selected" : "";
        return '<option value="' + pair.symbol + '"' + selected + ">" + pair.symbol + " - " + strategyLabel(pair.strategy) + "</option>";
      })
      .join("");
    state.chartSymbol = select.value || current;
    if (!state.chartLoaded) {
      state.chartLoaded = true;
      loadChart(state.chartSymbol);
    }
  }

  function renderSymbolStrip(pairs) {
    if (!pairs.length) {
      html("symbolStrip", '<span class="symbol-pill muted">No symbols configured</span>');
      return;
    }
    html(
      "symbolStrip",
      pairs
        .map(function (pair) {
          return '<span class="symbol-pill">' + pair.symbol + "</span>";
        })
        .join("")
    );
  }

  function renderPairs(pairs) {
    var grid = byId("pairGrid");
    if (!grid) {
      return;
    }
    grid.innerHTML = "";
    if (!pairs.length) {
      grid.innerHTML = '<div class="activity-item"><p>No configured symbols were returned by the server.</p></div>';
      return;
    }
    pairs.forEach(function (pair) {
      var latest = lastResultFor(pair.symbol);
      var signal = latest && latest.signal ? latest.signal : { side: "hold" };
      var side = signal.side || "hold";
      var card = document.createElement("article");
      card.className = "pair-card";
      card.innerHTML =
        '<div class="card-top">' +
        "<div>" +
        '<div class="symbol">' +
        pair.symbol +
        "</div>" +
        '<div class="strategy">' +
        strategyLabel(pair.strategy) +
        "</div>" +
        "</div>" +
        '<span class="signal ' +
        side +
        '">' +
        side +
        "</span>" +
        "</div>" +
        '<div class="card-meta">' +
        "<div><span>Interval</span><strong>" +
        pair.interval +
        "m</strong></div>" +
        "<div><span>Candles</span><strong>" +
        pair.candles +
        "</strong></div>" +
        "<div><span>Min base</span><strong>" +
        (pair.min_order_base || 0) +
        "</strong></div>" +
        "<div><span>Last price</span><strong>" +
        formatPrice(latest && latest.price) +
        "</strong></div>" +
        "</div>" +
        '<p class="message">' +
        (latest ? latest.message : "Waiting for first scan.") +
        "</p>" +
        '<button class="run-pair" type="button" data-symbol="' +
        pair.symbol +
        '">Run ' +
        pair.symbol +
        "</button>";
      grid.appendChild(card);
    });

    Array.prototype.forEach.call(document.querySelectorAll(".run-pair"), function (button) {
      button.addEventListener("click", function () {
        runPair(button.getAttribute("data-symbol"));
      });
    });
  }

  function renderActivity(history) {
    var list = byId("activityList");
    if (!list) {
      return;
    }
    var rows = [];
    history
      .slice()
      .reverse()
      .forEach(function (batch) {
        (batch.results || []).forEach(function (result) {
          rows.push({ batch: batch, result: result });
        });
      });
    rows = rows.slice(0, 24);

    if (!rows.length) {
      list.innerHTML = '<div class="activity-item"><p>No decisions yet. Run a scan to populate this feed.</p></div>';
      return;
    }

    list.innerHTML = "";
    rows.forEach(function (row) {
      var result = row.result;
      var signal = result.signal || { side: "hold", reason: "" };
      var item = document.createElement("article");
      item.className = "activity-item";
      item.innerHTML =
        '<div class="activity-top">' +
        "<strong>" +
        result.symbol +
        " at " +
        formatPrice(result.price) +
        "</strong>" +
        '<span class="signal ' +
        signal.side +
        '">' +
        signal.side +
        "</span>" +
        "</div>" +
        "<p>" +
        strategyLabel(result.strategy) +
        ": " +
        signal.reason +
        "</p>" +
        "<p>" +
        result.message +
        " - " +
        row.batch.iso_time +
        "</p>";
      list.appendChild(item);
    });
  }

  function refresh() {
    return api("/api/status").then(renderStatus);
  }

  function loadChart(symbol) {
    symbol = symbol || (byId("chartSymbol") ? byId("chartSymbol").value : "");
    if (!symbol) {
      return Promise.resolve();
    }
    state.chartSymbol = symbol;
    text("chartMeta", "Loading " + symbol + " candles...");
    showChartEmpty("Loading chart...");
    return api("/api/chart/" + encodeURIComponent(symbol))
      .then(function (payload) {
        drawChart(payload.chart);
        renderMarkers(payload.chart);
      })
      .catch(function (error) {
        showChartEmpty(error.message);
        toast(error.message);
      });
  }

  function showChartEmpty(message) {
    var empty = byId("chartEmpty");
    if (!empty) {
      return;
    }
    empty.textContent = message;
    empty.classList.remove("hidden");
  }

  function hideChartEmpty() {
    var empty = byId("chartEmpty");
    if (empty) {
      empty.classList.add("hidden");
    }
  }

  function drawChart(chart) {
    var canvas = byId("tradeChart");
    if (!canvas) {
      return;
    }
    var candles = chart.candles || [];
    var markers = chart.markers || [];
    if (!candles.length) {
      showChartEmpty("No candles returned for " + chart.symbol);
      return;
    }

    var rect = canvas.getBoundingClientRect();
    var dpr = window.devicePixelRatio || 1;
    var width = Math.max(320, Math.floor(rect.width || canvas.width));
    var height = Math.max(260, Math.floor(rect.height || canvas.height));
    canvas.width = width * dpr;
    canvas.height = height * dpr;
    var ctx = canvas.getContext("2d");
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
    ctx.clearRect(0, 0, width, height);

    var padLeft = 54;
    var padRight = 18;
    var padTop = 20;
    var padBottom = 34;
    var plotW = width - padLeft - padRight;
    var plotH = height - padTop - padBottom;
    var minPrice = Math.min.apply(
      null,
      candles.map(function (candle) {
        return Number(candle.low);
      })
    );
    var maxPrice = Math.max.apply(
      null,
      candles.map(function (candle) {
        return Number(candle.high);
      })
    );
    var pricePad = Math.max((maxPrice - minPrice) * 0.08, maxPrice * 0.002);
    minPrice -= pricePad;
    maxPrice += pricePad;

    function xForIndex(index) {
      if (candles.length === 1) {
        return padLeft + plotW / 2;
      }
      return padLeft + (index / (candles.length - 1)) * plotW;
    }

    function yForPrice(price) {
      return padTop + ((maxPrice - price) / (maxPrice - minPrice)) * plotH;
    }

    ctx.fillStyle = "#f9fbf8";
    ctx.fillRect(0, 0, width, height);
    ctx.strokeStyle = "#d9dfdb";
    ctx.lineWidth = 1;
    ctx.font = "12px system-ui, sans-serif";
    ctx.fillStyle = "#65706e";

    for (var i = 0; i <= 4; i += 1) {
      var y = padTop + (plotH / 4) * i;
      var price = maxPrice - ((maxPrice - minPrice) / 4) * i;
      ctx.beginPath();
      ctx.moveTo(padLeft, y);
      ctx.lineTo(width - padRight, y);
      ctx.stroke();
      ctx.fillText(formatPrice(price), 8, y + 4);
    }

    var candleW = Math.max(3, Math.min(12, plotW / candles.length * 0.58));
    candles.forEach(function (candle, index) {
      var x = xForIndex(index);
      var open = Number(candle.open);
      var close = Number(candle.close);
      var high = Number(candle.high);
      var low = Number(candle.low);
      var up = close >= open;
      var color = up ? "#087f5b" : "#b42318";
      var yOpen = yForPrice(open);
      var yClose = yForPrice(close);
      ctx.strokeStyle = color;
      ctx.fillStyle = color;
      ctx.beginPath();
      ctx.moveTo(x, yForPrice(high));
      ctx.lineTo(x, yForPrice(low));
      ctx.stroke();
      ctx.fillRect(x - candleW / 2, Math.min(yOpen, yClose), candleW, Math.max(2, Math.abs(yClose - yOpen)));
    });

    markers.forEach(function (marker) {
      var markerIndex = nearestCandleIndex(candles, Number(marker.time));
      var x = xForIndex(markerIndex);
      var y = yForPrice(Number(marker.price));
      var isEntry = marker.type === "entry";
      ctx.fillStyle = isEntry ? "#087f5b" : "#b42318";
      ctx.beginPath();
      if (isEntry) {
        ctx.moveTo(x, y - 12);
        ctx.lineTo(x - 7, y + 2);
        ctx.lineTo(x + 7, y + 2);
      } else {
        ctx.moveTo(x, y + 12);
        ctx.lineTo(x - 7, y - 2);
        ctx.lineTo(x + 7, y - 2);
      }
      ctx.closePath();
      ctx.fill();
      ctx.fillText(isEntry ? "Entry" : "Exit", Math.min(width - 70, x + 8), y - 8);
    });

    text("chartMeta", chart.symbol + " - " + strategyLabel(chart.strategy) + " - " + chart.interval + "m candles");
    hideChartEmpty();
  }

  function nearestCandleIndex(candles, timestamp) {
    var best = 0;
    var bestDistance = Infinity;
    candles.forEach(function (candle, index) {
      var distance = Math.abs(Number(candle.time) - timestamp);
      if (distance < bestDistance) {
        best = index;
        bestDistance = distance;
      }
    });
    return best;
  }

  function renderMarkers(chart) {
    var markers = chart.markers || [];
    if (!markers.length) {
      html("markerList", '<span class="marker-chip">No entry or exit markers yet. Run scans until a buy/sell signal appears.</span>');
      return;
    }
    html(
      "markerList",
      markers
        .slice()
        .reverse()
        .slice(0, 12)
        .map(function (marker) {
          var label = marker.type === "entry" ? "Entry" : marker.type === "exit" ? "Exit" : "Sell signal";
          var pnl = typeof marker.pnl_pct === "number" ? " P/L " + marker.pnl_pct + "%" : "";
          return (
            '<span class="marker-chip ' +
            marker.type +
            '">' +
            label +
            " " +
            formatPrice(marker.price) +
            " " +
            marker.iso_time +
            pnl +
            "</span>"
          );
        })
        .join("")
    );
  }

  function runAction(label, callback) {
    setBusy(true);
    return callback()
      .then(refresh)
      .then(function () {
        toast(label);
      })
      .catch(function (error) {
        toast(error.message);
        setBusy(false);
      })
      .then(function () {
        setBusy(false);
      });
  }

  function runPair(symbol) {
    return runAction(symbol + " scan complete", function () {
      return api("/api/run/" + encodeURIComponent(symbol), { method: "POST", body: "{}" });
    });
  }

  function bind(id, eventName, callback) {
    var element = byId(id);
    if (element) {
      element.addEventListener(eventName, callback);
    }
  }

  function activateTab(tabId) {
    Array.prototype.forEach.call(document.querySelectorAll(".tab-button"), function (button) {
      addClass(button, "active", button.getAttribute("data-tab") === tabId);
    });
    Array.prototype.forEach.call(document.querySelectorAll(".tab-view"), function (view) {
      addClass(view, "active", view.id === tabId);
    });
    if (tabId === "tradingView") {
      window.setTimeout(function () {
        loadChart((byId("chartSymbol") || {}).value || state.chartSymbol);
      }, 50);
    }
  }

  function boot() {
    Array.prototype.forEach.call(document.querySelectorAll(".tab-button"), function (button) {
      button.addEventListener("click", function () {
        activateTab(button.getAttribute("data-tab"));
      });
    });

    bind("runAllBtn", "click", function () {
      runAction("Scan complete", function () {
        return api("/api/run", { method: "POST", body: "{}" });
      });
    });

    bind("startLoopBtn", "click", function () {
      runAction("Loop started", function () {
        var sleep = Number((byId("sleepSeconds") || {}).value || 60);
        return api("/api/loop/start", {
          method: "POST",
          body: JSON.stringify({ sleep_seconds: sleep }),
        });
      });
    });

    bind("stopLoopBtn", "click", function () {
      runAction("Loop stopped", function () {
        return api("/api/loop/stop", { method: "POST", body: "{}" });
      });
    });

    bind("reloadBtn", "click", function () {
      runAction("Config reloaded", function () {
        return api("/api/reload", { method: "POST", body: "{}" });
      });
    });

    bind("startServiceBtn", "click", function () {
      runAction("Bot service started", function () {
        return api("/api/service/start", { method: "POST", body: "{}" });
      });
    });

    bind("stopServiceBtn", "click", function () {
      runAction("Bot service stopped", function () {
        return api("/api/service/stop", { method: "POST", body: "{}" });
      });
    });

    bind("apiForm", "submit", function (event) {
      event.preventDefault();
      runAction("Kraken API keys saved", function () {
        return api("/api/credentials", {
          method: "POST",
          body: JSON.stringify({
            api_key: (byId("apiKey") || {}).value || "",
            api_secret: (byId("apiSecret") || {}).value || "",
          }),
        }).then(function () {
          if (byId("apiKey")) {
            byId("apiKey").value = "";
          }
          if (byId("apiSecret")) {
            byId("apiSecret").value = "";
          }
        });
      });
    });

    bind("chartSymbol", "change", function () {
      loadChart((byId("chartSymbol") || {}).value || "");
    });

    bind("refreshChartBtn", "click", function () {
      loadChart((byId("chartSymbol") || {}).value || state.chartSymbol);
    });

    bind("runChartSymbolBtn", "click", function () {
      var symbol = (byId("chartSymbol") || {}).value || state.chartSymbol;
      runPair(symbol).then(function () {
        loadChart(symbol);
      });
    });

    bind("logoutBtn", "click", function () {
      fetch(base + "/api/logout", {
        method: "POST",
        credentials: "same-origin",
        headers: { "Content-Type": "application/json" },
        body: "{}",
      }).then(function () {
        window.location.href = base + "/login";
      });
    });

    refresh().catch(function (error) {
      html("pairGrid", '<div class="activity-item"><p>' + error.message + "</p></div>");
      toast(error.message);
    });
    window.setInterval(function () {
      refresh().catch(function () {});
    }, 15000);
  }

  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", boot);
  } else {
    boot();
  }
})();
