/* MPS-20 auction page live UI:
 * - Updates the countdown clock every second based on data-end-timestamp
 * - Polls /a/{id}.json every 5 seconds for state changes (current high,
 *   end_timestamp updates from soft-close, state transitions)
 * - Hooks the bid form to submit via fetch and update UI without a full
 *   page reload (progressive enhancement — form still works without JS)
 *
 * Per CLAUDE.md "capability-driven presentation" — the page works fine
 * without this script; the script enhances when JS is available.
 */
(function () {
  "use strict";

  const root = document.querySelector(".auction-page");
  if (!root) return;
  const auctionId = root.getAttribute("data-auction-id");
  if (!auctionId) return;

  const countdownEl = document.getElementById("auction-countdown");
  const currentHighEl = document.getElementById("auction-current-high");
  const flashEl = document.getElementById("auction-flash");
  const bidForm = document.getElementById("auction-bid-form");
  const buyNowForm = document.getElementById("auction-buy-now-form");

  /* fmtRemaining — prose human delta, matches ago.human() server-side.
   * Top two non-zero units, so the line stays short on small viewports.
   */
  function fmtRemaining(ms) {
    if (ms <= 0) return "ended";
    const totalSeconds = Math.floor(ms / 1000);
    const units = [
      { name: "day", value: Math.floor(totalSeconds / 86400) },
      { name: "hour", value: Math.floor((totalSeconds % 86400) / 3600) },
      { name: "minute", value: Math.floor((totalSeconds % 3600) / 60) },
      { name: "second", value: totalSeconds % 60 },
    ];
    const parts = [];
    for (let i = 0; i < units.length && parts.length < 2; i++) {
      const u = units[i];
      if (u.value === 0 && parts.length === 0 && i < units.length - 1) continue;
      parts.push(u.value + " " + u.name + (u.value === 1 ? "" : "s"));
    }
    return "in " + parts.join(", ");
  }

  function tickCountdown() {
    if (!countdownEl) return;
    const endRaw = countdownEl.getAttribute("data-end-timestamp");
    if (!endRaw) return;
    const end = parseInt(endRaw, 10);
    if (isNaN(end)) return;
    const remaining = end - Date.now();
    countdownEl.textContent = fmtRemaining(remaining);
  }

  function flashMessage(text, kind) {
    if (!flashEl) return;
    flashEl.textContent = text;
    flashEl.className = "auction-flash auction-flash-" + (kind || "info");
  }

  function applyState(state) {
    if (currentHighEl && typeof state.current_high === "number") {
      currentHighEl.textContent = "$" + state.current_high.toFixed(2);
    }
    if (countdownEl && state.end_timestamp) {
      countdownEl.setAttribute("data-end-timestamp", String(state.end_timestamp));
    }
    if (state.is_terminal) {
      // Disable bid + buy-now forms.
      [bidForm, buyNowForm].forEach((f) => {
        if (!f) return;
        const submit = f.querySelector('[type="submit"]');
        if (submit) submit.disabled = true;
      });
    }
  }

  let pollInterval = null;
  function poll() {
    fetch("/a/" + auctionId + ".json", { credentials: "same-origin" })
      .then(function (r) {
        if (!r.ok) throw new Error("status " + r.status);
        return r.json();
      })
      .then(applyState)
      .catch(function () {
        // Silent — non-fatal; user can refresh.
      });
  }

  if (bidForm) {
    bidForm.addEventListener("submit", function (e) {
      e.preventDefault();
      const data = new FormData(bidForm);
      fetch("/a/" + auctionId + "/bid", {
        method: "POST",
        body: data,
        credentials: "same-origin",
        headers: { "X-Requested-With": "XMLHttpRequest" },
      })
        .then(function (r) {
          return r.json().then(function (j) {
            return { status: r.status, body: j };
          });
        })
        .then(function (res) {
          if (res.status === 200 && res.body.ok) {
            flashMessage(
              "Bid placed at $" + res.body.bid_amount.toFixed(2) +
                (res.body.is_winning ? " — you are winning!" : " — outbid by proxy."),
              "success"
            );
            applyState(res.body.auction_state);
          } else {
            flashMessage(res.body.error || "Bid failed.", "error");
          }
        })
        .catch(function () {
          flashMessage("Network error placing bid.", "error");
        });
    });
  }

  /* Pay-by countdown (post-end). Rewrites any [data-pay-deadline]
   * element on the page in place — the server pre-fills the same
   * element with ago.human(), and we refine to second precision. */
  function tickPayCountdown() {
    const nodes = document.querySelectorAll("[data-pay-deadline]");
    if (!nodes.length) return;
    const now = Date.now();
    nodes.forEach(function (node) {
      const raw = node.getAttribute("data-pay-deadline");
      const deadline = parseInt(raw, 10);
      if (isNaN(deadline)) return;
      node.textContent = fmtRemaining(deadline - now);
    });
  }

  // Countdown ticks locally every second.
  setInterval(tickCountdown, 1000);
  setInterval(tickPayCountdown, 1000);
  tickCountdown();
  tickPayCountdown();

  // Live state: prefer a bounded SSE feed (the server closes it after
  // ~25s; EventSource reconnects on its own), and fall back to polling
  // /a/{id}.json every 5s where EventSource isn't available.
  if (window.EventSource) {
    var es = new EventSource("/a/" + auctionId + "/events");
    es.onmessage = function (e) {
      try {
        applyState(JSON.parse(e.data));
      } catch (err) {
        /* ignore malformed frame */
      }
    };
    es.addEventListener("gone", function () {
      es.close();
    });
    // 'reconnect' frames and transport errors are handled by
    // EventSource's own auto-reconnect — nothing to do here.
  } else {
    pollInterval = setInterval(poll, 5000);
  }
})();
