/**
 * public/free-license.js — free 30-day self-hosted license CTA.
 *
 * Owns: the POST /api/license/free mint flow + success/error rendering, and
 *       zip_downloaded tracking for release-zip links.
 * Does NOT own: minting itself (routes/license.js), pricing/plan UI, nav.
 *
 * Shared by BOTH /download (public/download.html — mints in place, since that is
 * where people land wanting to install) and /pricing (public/pricing.html — still
 * useful for someone comparing tiers). Pages differ only in element ids, the
 * signup redirect, and CTA label; everything else lives here so the two cannot
 * drift apart.
 *
 * Usage (after the markup, matching nav-component.js's tvNav.init convention):
 *   tvFreeLicense.init({ btnId, msgId, keyWrapId, keyId, expiryId, redirect, ctaLabel });
 *   tvFreeLicense.bindZipTracking(document.getElementById('some-zip-link'));
 *
 * No build step — plain ES5-ish browser JS, loaded with a <script src>.
 */
(function () {
  'use strict';

  // ── Release zip ──────────────────────────────────────────────────────────
  // The single source for the release version *in JS*. It must match the root
  // VERSION file, which is the single source of truth for the release (see the
  // "Versioning" section in CLAUDE.md). No build step exists, so a static page
  // cannot read VERSION at runtime — bump this together with the plain hrefs in
  // download.html (hero ghost button + curl block) and pricing.html (post-mint
  // step 1), which are hardcoded so they still work without JS.
  // NEVER bump these before the matching zip is live on R2 — the links 404.
  var VERSION  = '1.3.0';
  var FILENAME = 'tunevault-selfhosted-v' + VERSION + '.zip';
  var ZIP_URL  = 'https://releases.tunevault.app/' + FILENAME;

  var BILLING_URL = '/settings/billing';

  function esc(s) {
    return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
      return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
    });
  }

  /**
   * Fire zip_downloaded then navigate. Both call sites must send an identical
   * payload or the download funnel fractures across two sources.
   * Navigation always happens: on success, on failure, and on a timer.
   */
  function bindZipTracking(link) {
    if (!link) return;
    link.addEventListener('click', function (e) {
      var href = link.getAttribute('href');
      e.preventDefault();
      var done = false;
      function go() {
        if (done) return;
        done = true;
        window.location.href = href;
      }
      try {
        fetch('/api/analytics/event', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          keepalive: true,
          body: JSON.stringify({
            event_name: 'zip_downloaded',
            properties: { version: VERSION, filename: FILENAME }
          })
        }).then(go).catch(go);
      } catch (err) { go(); }
      setTimeout(go, 400);
    });
  }

  /**
   * Wire the mint CTA.
   * opts: { btnId, msgId, keyWrapId, keyId, expiryId, redirect, ctaLabel }
   * `redirect` is where signup should return the user — each page sends people
   * back to ITSELF, so a logged-out visitor lands where they started.
   */
  function init(opts) {
    var btn     = document.getElementById(opts.btnId);
    var msg     = document.getElementById(opts.msgId);
    var keyWrap = document.getElementById(opts.keyWrapId);
    var keyEl   = document.getElementById(opts.keyId);
    var expEl   = document.getElementById(opts.expiryId);
    if (!btn || !msg || !keyWrap || !keyEl) return;

    var ctaLabel = opts.ctaLabel || btn.textContent;

    function setMsg(html, color) {
      msg.innerHTML = html;
      msg.style.color = color || '';
    }

    function resetBtn() {
      btn.textContent = ctaLabel;
      btn.style.pointerEvents = '';
    }

    function showKey(data, alreadyIssued) {
      keyEl.textContent = data.license_key;
      keyWrap.style.display = 'block';
      if (expEl && data.expires_at) {
        var d = new Date(data.expires_at);
        expEl.textContent = 'Valid until ' + d.toLocaleDateString(undefined, { day: 'numeric', month: 'long', year: 'numeric' })
          + ' — after that, existing connections and reports stay readable, but new health checks stop.';
      }
      // Only claim an email was sent when the server says it actually sent one.
      if (alreadyIssued) {
        setMsg('You already have a free license — here it is again.', 'var(--green)');
      } else if (data.emailed) {
        setMsg('Done — your key is also on its way to your inbox.', 'var(--green)');
      } else {
        setMsg('Done — copy your key below (we could not send the email; it is shown here).', 'var(--green)');
      }
      btn.style.display = 'none';
    }

    // A paying customer asking for a trial is not an error state — say so plainly.
    // Their key is in the purchase email: no page renders it, so do NOT pretend
    // billing shows it; link billing for plan management and offer a resend.
    function showPaid(body) {
      var tier = body.plan_tier ? esc(body.plan_tier) + ' ' : '';
      setMsg(
        'You already have an active ' + tier + 'license — no trial needed. Your key was emailed to you when you subscribed.<br>' +
        '<a href="' + BILLING_URL + '" style="color:var(--green);">Manage your plan</a> &middot; ' +
        'Need the key resent? <a href="mailto:kiran@tunevault.app" style="color:var(--green);">kiran@tunevault.app</a>',
        'var(--text-dim, #b8b8c8)'
      );
      btn.style.display = 'none';
    }

    function claim(e) {
      e.preventDefault();
      btn.textContent = 'Issuing…';
      btn.style.pointerEvents = 'none';
      setMsg('', 'var(--text-muted)');

      fetch('/api/license/free', { method: 'POST', credentials: 'same-origin' })
        .then(function (r) { return r.json().then(function (b) { return { ok: r.ok, body: b || {} }; }); })
        .then(function (res) {
          if (res.ok) return showKey(res.body, !!res.body.already_issued);
          if (res.body.code === 'paid_license_active') return showPaid(res.body);
          setMsg(esc(res.body.error || 'Could not issue a license. Please try again.'), '#f87171');
          resetBtn();
        })
        .catch(function () {
          setMsg('Network error — please try again.', '#f87171');
          resetBtn();
        });
    }

    // Logged out keeps the anchor's default href (/signup?redirect=<this page>).
    if (opts.redirect) btn.setAttribute('href', '/signup?redirect=' + opts.redirect);

    fetch('/api/auth/me', { credentials: 'same-origin' })
      .then(function (r) { return r.ok ? r.json() : null; })
      .then(function (user) {
        if (user) btn.addEventListener('click', claim);
      })
      .catch(function () { /* stay logged-out: href still goes to signup */ });
  }

  window.tvFreeLicense = {
    init: init,
    bindZipTracking: bindZipTracking,
    VERSION: VERSION,
    FILENAME: FILENAME,
    ZIP_URL: ZIP_URL
  };
})();
