// Share Modal — DC-058
//
// Admin UI for DC-053 share routes. Opened from the "Share" button on each
// service card (added in core/grid.js next to the existing options/delete
// buttons). Two tabs:
//   - Public link: pick a TTL (1h/24h/7d), POST /api/v1/share, render the
//     returned urlPath with a copy button + revoke control.
//   - Tailscale invite: enter an email, POST /api/v1/share/tailscale, render
//     delivered status (inbox vs dev-console fallback vs URL fallback).
//
// Modal also lists outstanding shares for the selected service (GET /api/v1/share)
// with revoke buttons. The list refreshes after every issue/revoke.
//
// Both issue endpoints are Pro-gated on the server — the modal surfaces a 402
// as an upgrade prompt ("Share is a Pro feature. Activate a license to unlock.").
// Tailscale invites additionally require tailscaleCoord configured on the host;
// the server returns 400 with a clear message when missing.

(function() {
  'use strict';

  if (window.__dc_058_share_modal_loaded) return;
  window.__dc_058_share_modal_loaded = true;

  const TTL_OPTIONS = [
    { ms: 60 * 60 * 1000, label: '1 hour' },
    { ms: 24 * 60 * 60 * 1000, label: '24 hours' },
    { ms: 7 * 24 * 60 * 60 * 1000, label: '7 days' },
  ];

  injectModal('share-modal', `
    <div id="share-modal" class="weather-modal" role="dialog" aria-labelledby="share-modal-title">
      <div class="weather-modal-content" style="min-width: 460px; max-width: 580px;">
        <h3 id="share-modal-title">Share <span id="share-modal-service-name">…</span></h3>
        <p style="font-size: 0.85rem; color: var(--muted); margin: 0 0 16px;">
          Create a public link or Tailscale invite so someone outside your network can access this service.
        </p>

        <div class="share-tabs" style="display: flex; gap: 4px; margin-bottom: 16px; border-bottom: 1px solid var(--border);">
          <button class="share-tab active" data-tab="public" type="button"
            style="background: transparent; border: 0; border-bottom: 2px solid var(--accent); padding: 8px 14px; color: var(--fg); font-weight: 600; cursor: pointer;">
            Public link
          </button>
          <button class="share-tab" data-tab="tailscale" type="button"
            style="background: transparent; border: 0; border-bottom: 2px solid transparent; padding: 8px 14px; color: var(--muted); cursor: pointer;">
            Tailscale invite
          </button>
        </div>

        <div class="share-tab-panel" data-panel="public">
          <label class="form-label-bold" for="share-public-ttl">Link duration:</label>
          <select id="share-public-ttl" style="width: 100%; padding: 8px 10px; margin: 6px 0 12px; background: var(--input-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 4px;">
            ${TTL_OPTIONS.map(o => `<option value="${o.ms}">${o.label}</option>`).join('')}
          </select>
          <button id="share-public-create" class="btn-accent" type="button"
            style="width: 100%; padding: 10px; background: var(--accent); color: #04141f; border: 0; border-radius: 4px; font-weight: 600; cursor: pointer;">
            Create share link
          </button>
        </div>

        <div class="share-tab-panel" data-panel="tailscale" style="display: none;">
          <label class="form-label-bold" for="share-ts-email">Recipient email:</label>
          <input id="share-ts-email" type="email" placeholder="alice@example.com" autocomplete="off"
            style="width: 100%; padding: 10px 12px; margin: 6px 0 12px; background: var(--card-bg); color: var(--fg); border: 1px solid var(--border); border-radius: 4px; font-size: 0.95rem;" />
          <p style="font-size: 0.8rem; color: var(--muted); margin: 0 0 12px;">
            A single-use Tailscale pre-auth key is generated and emailed. The device joins your tailnet and is routed to this service via Caddy.
          </p>
          <button id="share-ts-create" class="btn-accent" type="button"
            style="width: 100%; padding: 10px; background: var(--accent); color: #04141f; border: 0; border-radius: 4px; font-weight: 600; cursor: pointer;">
            Create Tailscale invite
          </button>
        </div>

        <div id="share-issued" style="display: none; margin-top: 16px; padding: 12px; background: var(--card-bg); border: 1px solid var(--border); border-radius: 6px;">
          <div style="font-size: 0.8rem; color: var(--muted); margin-bottom: 6px;">Share link:</div>
          <div style="display: flex; gap: 6px; align-items: center;">
            <input id="share-issued-url" type="text" readonly
              style="flex: 1; padding: 8px 10px; background: var(--bg); color: var(--fg); border: 1px solid var(--border); border-radius: 4px; font-family: monospace; font-size: 0.85rem;" />
            <button id="share-issued-copy" type="button"
              style="padding: 8px 14px; background: var(--accent); color: #04141f; border: 0; border-radius: 4px; font-weight: 600; cursor: pointer;">
              Copy
            </button>
          </div>
          <div id="share-issued-meta" style="font-size: 0.8rem; color: var(--muted); margin-top: 8px;"></div>
        </div>

        <div id="share-error" style="display: none; margin-top: 12px; padding: 10px; border-radius: 4px; background: rgba(231,76,60,0.15); color: var(--bad-fg); font-size: 0.85rem;"></div>
        <div id="share-success" style="display: none; margin-top: 12px; padding: 10px; border-radius: 4px; background: rgba(46,204,113,0.15); color: var(--ok-fg); font-size: 0.85rem;"></div>

        <div id="share-outstanding" style="margin-top: 16px;">
          <label class="form-label-bold">Outstanding shares for this service</label>
          <div id="share-outstanding-list" style="margin-top: 6px; font-size: 0.85rem;"></div>
        </div>

        <div class="weather-modal-buttons" style="margin-top: 18px;">
          <button id="share-cancel" type="button">Close</button>
        </div>
      </div>
    </div>
  `);

  const modal = document.getElementById('share-modal');
  const serviceNameEl = document.getElementById('share-modal-service-name');
  const issuedEl = document.getElementById('share-issued');
  const issuedUrlInput = document.getElementById('share-issued-url');
  const issuedCopyBtn = document.getElementById('share-issued-copy');
  const issuedMetaEl = document.getElementById('share-issued-meta');
  const errorEl = document.getElementById('share-error');
  const successEl = document.getElementById('share-success');
  const outstandingListEl = document.getElementById('share-outstanding-list');
  const cancelBtn = document.getElementById('share-cancel');
  const publicCreateBtn = document.getElementById('share-public-create');
  const tsCreateBtn = document.getElementById('share-ts-create');
  const tsEmailInput = document.getElementById('share-ts-email');
  const publicTtlSelect = document.getElementById('share-public-ttl');

  let currentService = null; // { id, name }
  let activeTab = 'public';

  function hideMessages() {
    errorEl.style.display = 'none';
    successEl.style.display = 'none';
    issuedEl.style.display = 'none';
  }

  function showError(msg) {
    successEl.style.display = 'none';
    issuedEl.style.display = 'none';
    errorEl.textContent = msg;
    errorEl.style.display = 'block';
  }

  function showSuccess(msg) {
    errorEl.style.display = 'none';
    successEl.textContent = msg;
    successEl.style.display = 'block';
  }

  function _originFromPage() {
    // Build the absolute share URL from the page's current origin so the
    // link is correct regardless of whether the user is on http://localhost
    // (dev) or https://status.sami (prod). The urlPath returned by the API
    // is a path-only string starting with /share/.
    return window.location.origin;
  }

  function _absUrl(urlPath) {
    if (urlPath.startsWith('http')) return urlPath;
    return _originFromPage() + urlPath;
  }

  function _formatExpiry(iso) {
    if (!iso) return '';
    const d = new Date(iso);
    if (Number.isNaN(d.getTime())) return '';
    return d.toLocaleString();
  }

  function _formatRemaining(ms) {
    if (ms <= 0) return 'expired';
    const h = Math.floor(ms / 3600000);
    if (h >= 24) return `${Math.floor(h / 24)}d ${h % 24}h left`;
    const m = Math.floor((ms % 3600000) / 60000);
    return `${h}h ${m}m left`;
  }

  function setActiveTab(name) {
    activeTab = name;
    hideMessages();
    modal.querySelectorAll('.share-tab').forEach(btn => {
      const isActive = btn.dataset.tab === name;
      btn.classList.toggle('active', isActive);
      btn.style.borderBottomColor = isActive ? 'var(--accent)' : 'transparent';
      btn.style.color = isActive ? 'var(--fg)' : 'var(--muted)';
      btn.style.fontWeight = isActive ? '600' : '400';
    });
    modal.querySelectorAll('.share-tab-panel').forEach(p => {
      p.style.display = p.dataset.panel === name ? '' : 'none';
    });
  }

  async function loadOutstanding() {
    outstandingListEl.innerHTML = '<span style="color: var(--muted);">Loading…</span>';
    try {
      const resp = await fetch('/api/v1/share', { credentials: 'same-origin' });
      if (!resp.ok) {
        outstandingListEl.innerHTML = '<span style="color: var(--muted);">No shares listed.</span>';
        return;
      }
      const body = await resp.json();
      const list = (body && body.data) || [];
      const filtered = list.filter(s => s.serviceId === currentService.id);
      if (filtered.length === 0) {
        outstandingListEl.innerHTML = '<span style="color: var(--muted);">No outstanding shares for this service.</span>';
        return;
      }
      outstandingListEl.innerHTML = filtered.map(s => {
        const remaining = s.expiresAt ? _formatRemaining(new Date(s.expiresAt).getTime() - Date.now()) : '';
        return `
          <div class="share-row" data-share-id="${escapeHtml(s.id)}"
            style="display: flex; align-items: center; gap: 8px; padding: 6px 0; border-bottom: 1px dashed var(--border);">
            <span style="flex: 1;">
              <strong>${s.kind === 'tailscale' ? 'Tailscale' : 'Public'}</strong>
              <span style="color: var(--muted);"> · expires ${escapeHtml(_formatExpiry(s.expiresAt))} (${remaining})</span>
            </span>
            <button class="share-revoke" data-share-id="${escapeHtml(s.id)}" type="button"
              style="padding: 4px 10px; background: transparent; color: var(--bad-fg); border: 1px solid var(--bad-fg); border-radius: 4px; cursor: pointer; font-size: 0.8rem;">
              Revoke
            </button>
          </div>
        `;
      }).join('');
      outstandingListEl.querySelectorAll('.share-revoke').forEach(btn => {
        btn.addEventListener('click', () => revokeShare(btn.dataset.shareId));
      });
    } catch (e) {
      outstandingListEl.innerHTML = '<span style="color: var(--muted);">Could not load outstanding shares.</span>';
    }
  }

  async function revokeShare(id) {
    try {
      const resp = await fetch(`/api/v1/share/${encodeURIComponent(id)}`, {
        method: 'DELETE',
        credentials: 'same-origin',
      });
      if (!resp.ok) {
        const body = await resp.json().catch(() => ({}));
        showError(body && body.error || `Revoke failed (HTTP ${resp.status}).`);
        return;
      }
      showSuccess('Share revoked.');
      loadOutstanding();
    } catch (e) {
      showError('Network error while revoking.');
    }
  }

  async function issuePublic() {
    if (!currentService) return;
    hideMessages();
    publicCreateBtn.disabled = true;
    const original = publicCreateBtn.textContent;
    publicCreateBtn.textContent = 'Creating…';
    try {
      const ttlMs = parseInt(publicTtlSelect.value, 10);
      const resp = await fetch('/api/v1/share', {
        method: 'POST',
        credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ serviceId: currentService.id, ttlMs }),
      });
      const body = await resp.json().catch(() => ({}));
      if (!resp.ok || !body.success) {
        _renderApiError(resp.status, body);
        return;
      }
      const urlPath = body.data.urlPath;
      issuedUrlInput.value = _absUrl(urlPath);
      issuedMetaEl.textContent = `Public link · expires ${_formatExpiry(body.data.expiresAt)}`;
      issuedEl.style.display = 'block';
      showSuccess('Share link created.');
      loadOutstanding();
    } catch (e) {
      showError('Network error while creating share.');
    } finally {
      publicCreateBtn.disabled = false;
      publicCreateBtn.textContent = original;
    }
  }

  async function issueTailscale() {
    if (!currentService) return;
    const email = (tsEmailInput.value || '').trim();
    if (!email || !email.includes('@')) {
      showError('A valid recipient email is required.');
      return;
    }
    hideMessages();
    tsCreateBtn.disabled = true;
    const original = tsCreateBtn.textContent;
    tsCreateBtn.textContent = 'Creating…';
    try {
      const resp = await fetch('/api/v1/share/tailscale', {
        method: 'POST',
        credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ serviceId: currentService.id, email }),
      });
      const body = await resp.json().catch(() => ({}));
      if (!resp.ok || !body.success) {
        _renderApiError(resp.status, body);
        return;
      }
      // Tailscale shares show the raw urlPath (which is the share URL) if
      // email delivery failed. The share itself is one-shot so the admin
      // can manually paste the link into a chat if SMTP failed.
      const fallback = body.data && body.data.urlPath;
      if (fallback) {
        issuedUrlInput.value = _absUrl(fallback);
        issuedMetaEl.textContent = `Tailscale invite · emailed to ${email} (or use this URL manually if delivery failed).`;
      } else {
        issuedUrlInput.value = '';
        issuedMetaEl.textContent = `Tailscale invite sent to ${email}.`;
      }
      issuedEl.style.display = 'block';
      showSuccess('Tailscale invite created.');
      tsEmailInput.value = '';
      loadOutstanding();
    } catch (e) {
      showError('Network error while creating Tailscale invite.');
    } finally {
      tsCreateBtn.disabled = false;
      tsCreateBtn.textContent = original;
    }
  }

  function _renderApiError(status, body) {
    const err = (body && body.error) || '';
    if (status === 402) {
      showError('Share is a Pro feature. Activate a license to unlock.');
      return;
    }
    if (status === 400 && /tailscale/i.test(err)) {
      showError('Tailscale is not configured on this host. Set up Tailscale in the dashboard first.');
      return;
    }
    if (status === 403) {
      showError('You do not have permission to share services.');
      return;
    }
    if (status === 404) {
      showError('Service not found. It may have been deleted.');
      return;
    }
    showError(err || `Request failed (HTTP ${status}).`);
  }

  function openShareModal(service) {
    if (!service || !service.id) return;
    currentService = { id: service.id, name: service.name || service.id };
    serviceNameEl.textContent = currentService.name;
    hideMessages();
    setActiveTab('public');
    tsEmailInput.value = '';
    publicTtlSelect.value = String(TTL_OPTIONS[1].ms); // 24h default
    modal.classList.add('show');
    loadOutstanding();
  }

  // Wire events
  modal.querySelectorAll('.share-tab').forEach(btn => {
    btn.addEventListener('click', () => setActiveTab(btn.dataset.tab));
  });
  publicCreateBtn.addEventListener('click', issuePublic);
  tsCreateBtn.addEventListener('click', issueTailscale);
  cancelBtn.addEventListener('click', () => modal.classList.remove('show'));
  wireModal(modal, cancelBtn);

  // Copy-to-clipboard for the issued share URL
  issuedCopyBtn.addEventListener('click', async () => {
    const url = issuedUrlInput.value;
    if (!url) return;
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) {
        await navigator.clipboard.writeText(url);
      } else {
        // Fallback for older browsers / unsafe contexts
        issuedUrlInput.select();
        document.execCommand('copy');
      }
      const original = issuedCopyBtn.textContent;
      issuedCopyBtn.textContent = 'Copied!';
      setTimeout(() => { issuedCopyBtn.textContent = original; }, 1200);
    } catch (e) {
      showError('Could not copy to clipboard. Select the URL manually.');
    }
  });

  // Expose for other modules to open
  window.openShareModal = openShareModal;
})();
