/**
 * session.js — Workshop Session Mode UI (Fund once, ask many)
 *
 * Flow:
 *  1. User clicks "⚡ FUND SESSION" → left panel slides in
 *  2. Picks amount (200–5000 sats) → POST /api/sessions → deposit invoice
 *  3. Pays invoice (stub: "Simulate Payment") → 2 s polling until state=active
 *  4. Macaroon + sessionId stored in localStorage; input bar activates (green border)
 *  5. Every Enter/Send in the input bar → POST /api/sessions/:id/request
 *  6. Timmy's response shown in speech bubble, balance ticks down in HUD
 *  7. Balance < 50 sats → low-balance notice; Top Up button reopens panel topup step
 *  8. On page reload: localStorage is read, session is validated, UI restored
 */

import { setSpeechBubble, setMood } from './agents.js';
import { appendSystemMessage, setSessionSendHandler, setInputBarSessionMode } from './ui.js';
import { getOrRefreshToken } from './nostr-identity.js';
import { sentiment } from './edge-worker-client.js';

const API          = '/api';
const LS_KEY       = 'timmy_session_v1';
const POLL_MS      = 2000;
const POLL_TIMEOUT = 60_000;
const MIN_BALANCE  = 50;

// ── Module state ──────────────────────────────────────────────────────────────
let _panel         = null;
let _sessionId     = null;
let _macaroon      = null;
let _balanceSats   = 0;
let _sessionState  = null; // null | 'awaiting_payment' | 'active' | 'paused' | 'expired'
let _pollTimer     = null;
let _inFlight      = false;
let _selectedSats  = 500;  // deposit amount selection
let _topupSats     = 500;  // topup amount selection

// ── Public API ────────────────────────────────────────────────────────────────

export function initSessionPanel() {
  _panel = document.getElementById('session-panel');
  if (!_panel) return;

  // Buttons
  _on('open-session-btn',       'click', _openPanel);
  _on('session-close',          'click', _closePanel);
  _on('session-create-btn',     'click', _createSession);
  _on('session-pay-btn',        'click', _payDeposit);
  _on('session-topup-btn',      'click', () => _setStep('topup'));
  _on('session-topup-create-btn','click', _createTopup);
  _on('session-topup-pay-btn',  'click', _payTopup);
  _on('session-back-btn',       'click', () => _setStep('active'));
  _on('topup-quick-btn',        'click', () => { _openPanel(); _setStep('topup'); });
  _on('session-hud-topup',     'click', (e) => { e.preventDefault(); _openPanel(); _setStep('topup'); });

  // Amount preset buttons — deposit (quick-fill the number input)
  _panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn').forEach(btn => {
    btn.addEventListener('click', () => {
      _panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn')
            .forEach(b => b.classList.remove('active'));
      btn.classList.add('active');
      _selectedSats = parseInt(btn.dataset.sats, 10);
      const inp = document.getElementById('session-amount-input');
      if (inp) inp.value = _selectedSats;
    });
  });

  // Free-text number input — deposit
  document.getElementById('session-amount-input')?.addEventListener('input', () => {
    const v = parseInt(document.getElementById('session-amount-input').value, 10);
    if (Number.isFinite(v)) {
      _selectedSats = v;
      _panel.querySelectorAll('[data-session-step="fund"] .session-amount-btn')
            .forEach(b => b.classList.toggle('active', parseInt(b.dataset.sats, 10) === v));
    }
  });

  // Amount preset buttons — topup (quick-fill the topup number input)
  _panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn').forEach(btn => {
    btn.addEventListener('click', () => {
      _panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn')
            .forEach(b => b.classList.remove('active'));
      btn.classList.add('active');
      _topupSats = parseInt(btn.dataset.sats, 10);
      const inp = document.getElementById('session-topup-input');
      if (inp) inp.value = _topupSats;
    });
  });

  // Free-text number input — topup
  document.getElementById('session-topup-input')?.addEventListener('input', () => {
    const v = parseInt(document.getElementById('session-topup-input').value, 10);
    if (Number.isFinite(v)) {
      _topupSats = v;
      _panel.querySelectorAll('[data-session-step="topup"] .session-amount-btn')
            .forEach(b => b.classList.toggle('active', parseInt(b.dataset.sats, 10) === v));
    }
  });

  // Try to restore from localStorage on init
  _tryRestore();
}

export function isSessionActive() {
  return _sessionState === 'active' || _sessionState === 'paused';
}

// Called by ui.js when user submits the input bar while session is active
export async function sessionSendHandler(text) {
  if (!_sessionId || !_macaroon || _inFlight) return;

  _inFlight = true;
  _setSendBusy(true);
  appendSystemMessage(`you: ${text}`);

  // Attach Nostr token if available
  const nostrToken = await getOrRefreshToken('/api');
  const reqHeaders = {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${_macaroon}`,
  };
  if (nostrToken) reqHeaders['X-Nostr-Token'] = nostrToken;

  try {
    const res = await fetch(`${API}/sessions/${_sessionId}/request`, {
      method:  'POST',
      headers: reqHeaders,
      body: JSON.stringify({ request: text }),
    });

    const data = await res.json();

    if (res.status === 402) {
      _balanceSats  = data.balance ?? 0;
      _sessionState = 'paused';
      _saveToStorage();
      _applySessionUI();
      setSpeechBubble('My energy is depleted... top me up to continue!');
      appendSystemMessage('⚡ Low balance — tap Top Up');
      return;
    }

    if (res.status === 401) {
      // Macaroon rejected — stale session, clear and reset
      _clearSession();
      appendSystemMessage('Session expired — fund a new one.');
      return;
    }

    if (!res.ok) {
      appendSystemMessage('Session error: ' + (data.error || res.status));
      return;
    }

    _balanceSats  = data.balanceRemaining ?? 0;
    _sessionState = _balanceSats < MIN_BALANCE ? 'paused' : 'active';
    _saveToStorage();
    _applySessionUI();

    const reply = data.result || data.reason || '…';
    setSpeechBubble(reply);
    appendSystemMessage('Timmy: ' + reply.slice(0, 80));

    // Sentiment-driven mood on inbound Timmy reply
    sentiment(reply).then(s => {
      setMood(s.label);
      setTimeout(() => setMood(null), 10_000);
    }).catch(() => {});

    // Update active-step balance if panel is open
    _updateActiveStep();

  } catch (err) {
    appendSystemMessage('Session error: ' + err.message);
  } finally {
    _inFlight = false;
    _setSendBusy(false);
  }
}

// ── Panel open/close ──────────────────────────────────────────────────────────

function _openPanel() {
  if (!_panel) return;
  _panel.classList.add('open');
  _clearError();
  if (_sessionState === 'active') {
    _setStep('active');
    _updateActiveStep();
  } else if (_sessionState === 'paused') {
    _setStep('topup');
  } else {
    _setStep('fund');
  }
}

function _closePanel() {
  _panel?.classList.remove('open');
  _stopPolling();
}

// ── Create session ────────────────────────────────────────────────────────────

async function _createSession() {
  _clearError();
  // Read from the number input (may differ from the last preset clicked)
  const inp = document.getElementById('session-amount-input');
  if (inp) {
    const v = parseInt(inp.value, 10);
    if (Number.isFinite(v)) _selectedSats = v;
  }
  if (_selectedSats < 200 || _selectedSats > 10_000) {
    _setError('Amount must be 200–10,000 sats.');
    return;
  }
  _setStatus('fund', 'creating session…', '#ffaa00');
  _btn('session-create-btn', true);

  try {
    const nostrToken = await getOrRefreshToken('/api');
    const createHeaders = { 'Content-Type': 'application/json' };
    if (nostrToken) createHeaders['X-Nostr-Token'] = nostrToken;

    const res  = await fetch(`${API}/sessions`, {
      method:  'POST',
      headers: createHeaders,
      body:    JSON.stringify({ amount_sats: _selectedSats }),
    });
    const data = await res.json();

    if (!res.ok) { _setError(data.error || 'Failed to create session.'); return; }

    _sessionId = data.sessionId;

    // Show deposit invoice
    const inv = data.invoice;
    _el('session-invoice-amount').textContent = inv.amountSats + ' sats';
    _el('session-invoice-pr').textContent     = inv.paymentRequest || '';
    const hashEl = _el('session-invoice-hash');
    if (hashEl) hashEl.dataset.hash = inv.paymentHash || '';

    _setStep('invoice');
    _setStatus('invoice', '⚡ Awaiting deposit…', '#ffaa00');

  } catch (err) {
    _setError('Network error: ' + err.message);
  } finally {
    _btn('session-create-btn', false);
  }
}

// ── Pay deposit (stub) ────────────────────────────────────────────────────────

async function _payDeposit() {
  const hash = _el('session-invoice-hash')?.dataset.hash;
  if (!hash) { _setError('No payment hash — use real Lightning?'); return; }

  _btn('session-pay-btn', true);
  _setStatus('invoice', 'Simulating payment…', '#ffaa00');

  try {
    const res = await fetch(`${API}/dev/stub/pay/${hash}`, { method: 'POST' });
    const ok  = (await res.json()).ok;
    if (!ok) { _setError('Payment simulation failed.'); return; }
    _startDepositPolling();
  } catch (err) {
    _setError('Payment error: ' + err.message);
  } finally {
    _btn('session-pay-btn', false);
  }
}

// ── Deposit polling ───────────────────────────────────────────────────────────

function _startDepositPolling() {
  _stopPolling();
  const deadline = Date.now() + POLL_TIMEOUT;

  async function poll() {
    if (!_sessionId) return;
    try {
      const nostrToken = await getOrRefreshToken('/api');
      const pollHeaders = nostrToken ? { 'X-Nostr-Token': nostrToken } : {};
      const res  = await fetch(`${API}/sessions/${_sessionId}`, { headers: pollHeaders });
      const data = await res.json();

      if (data.state === 'active') {
        _macaroon     = data.macaroon;
        _balanceSats  = data.balanceSats;
        _sessionState = 'active';
        _saveToStorage();
        _applySessionUI();
        _closePanel(); // panel auto-closes; user types in input bar
        appendSystemMessage(`Session active — ${_balanceSats} sats`);
        return;
      }
    } catch { /* network hiccup */ }

    if (Date.now() > deadline) {
      _setError('Timed out waiting for payment.');
      return;
    }
    _pollTimer = setTimeout(poll, POLL_MS);
  }

  _pollTimer = setTimeout(poll, POLL_MS);
}

// ── Topup ─────────────────────────────────────────────────────────────────────

async function _createTopup() {
  if (!_sessionId || !_macaroon) return;
  _clearError();
  // Read from the topup number input
  const inp = document.getElementById('session-topup-input');
  if (inp) {
    const v = parseInt(inp.value, 10);
    if (Number.isFinite(v)) _topupSats = v;
  }
  if (_topupSats < 200 || _topupSats > 10_000) {
    _setError('Topup amount must be 200–10,000 sats.');
    return;
  }
  _setStatus('topup', 'creating topup invoice…', '#ffaa00');
  _btn('session-topup-create-btn', true);

  try {
    const nostrToken = await getOrRefreshToken('/api');
    const topupHeaders = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${_macaroon}`,
    };
    if (nostrToken) topupHeaders['X-Nostr-Token'] = nostrToken;

    const res  = await fetch(`${API}/sessions/${_sessionId}/topup`, {
      method:  'POST',
      headers: topupHeaders,
      body: JSON.stringify({ amount_sats: _topupSats }),
    });
    const data = await res.json();

    if (!res.ok) {
      // Pending topup already exists — surface it
      const pending = data.pendingTopup;
      if (pending) {
        _el('session-topup-pr').textContent = pending.paymentRequest || '';
        const th = _el('session-topup-hash');
        if (th) th.dataset.hash = pending.paymentHash || '';
        _el('session-topup-pr-row').style.display = '';
        _setStatus('topup', '⚡ Previous invoice still pending', '#ffaa00');
        return;
      }
      _setError(data.error || 'Failed to create topup.');
      return;
    }

    const inv = data.topup;
    _el('session-topup-pr').textContent = inv.paymentRequest || '';
    const th = _el('session-topup-hash');
    if (th) th.dataset.hash = inv.paymentHash || '';
    _el('session-topup-pr-row').style.display = '';
    _setStatus('topup', '⚡ Awaiting topup payment…', '#ffaa00');

  } catch (err) {
    _setError('Topup error: ' + err.message);
  } finally {
    _btn('session-topup-create-btn', false);
  }
}

async function _payTopup() {
  const hash = _el('session-topup-hash')?.dataset.hash;
  if (!hash) { _setError('No topup hash.'); return; }

  _btn('session-topup-pay-btn', true);
  _setStatus('topup', 'Simulating topup payment…', '#ffaa00');

  try {
    const res = await fetch(`${API}/dev/stub/pay/${hash}`, { method: 'POST' });
    const ok  = (await res.json()).ok;
    if (!ok) { _setError('Topup simulation failed.'); return; }
    _startTopupPolling();
  } catch (err) {
    _setError('Topup payment error: ' + err.message);
  } finally {
    _btn('session-topup-pay-btn', false);
  }
}

function _startTopupPolling() {
  _stopPolling();
  const deadline = Date.now() + POLL_TIMEOUT;
  const prevBalance = _balanceSats;

  async function poll() {
    if (!_sessionId) return;
    try {
      const nostrToken = await getOrRefreshToken('/api');
      const pollHeaders = nostrToken ? { 'X-Nostr-Token': nostrToken } : {};
      const res  = await fetch(`${API}/sessions/${_sessionId}`, { headers: pollHeaders });
      const data = await res.json();

      if (data.balanceSats > prevBalance || data.state === 'active') {
        _balanceSats  = data.balanceSats;
        _macaroon     = data.macaroon || _macaroon;
        _sessionState = data.state === 'active' ? 'active' : _sessionState;
        _saveToStorage();
        _applySessionUI();
        _setStep('active');
        _updateActiveStep();
        _setStatus('active', `⚡ Topped up! ${_balanceSats} sats`, '#22aa66');
        return;
      }
    } catch { /* ignore */ }

    if (Date.now() > deadline) { _setError('Topup timed out.'); return; }
    _pollTimer = setTimeout(poll, POLL_MS);
  }

  _pollTimer = setTimeout(poll, POLL_MS);
}

// ── Restore from localStorage ─────────────────────────────────────────────────

async function _tryRestore() {
  try {
    const raw = localStorage.getItem(LS_KEY);
    if (!raw) return;
    const { sessionId, macaroon, balanceSats } = JSON.parse(raw);
    if (!sessionId || !macaroon) return;

    // Validate the session is still live
    const nostrToken = await getOrRefreshToken('/api');
    const restoreHeaders = nostrToken ? { 'X-Nostr-Token': nostrToken } : {};
    const res  = await fetch(`${API}/sessions/${sessionId}`, { headers: restoreHeaders });
    if (!res.ok) { localStorage.removeItem(LS_KEY); return; }
    const data = await res.json();

    if (data.state !== 'active' && data.state !== 'paused') {
      localStorage.removeItem(LS_KEY);
      return;
    }

    _sessionId    = sessionId;
    _macaroon     = data.macaroon || macaroon;
    _balanceSats  = data.balanceSats ?? balanceSats;
    _sessionState = data.state;
    _saveToStorage();
    _applySessionUI();

    appendSystemMessage(`Session restored (${_balanceSats} sats)`);
  } catch {
    // Ignore restore errors silently
  }
}

// ── Session UI helpers ────────────────────────────────────────────────────────

function _applySessionUI() {
  const anyActive = isSessionActive(); // true for both 'active' and 'paused'
  const canSend   = _sessionState === 'active';
  // Show low-balance overlay whenever session exists but balance is too low
  const lowBal    = anyActive && _balanceSats < MIN_BALANCE;

  // HUD balance: "Balance: X sats  ⚡ Top Up"
  const $hud = document.getElementById('session-hud');
  if ($hud) {
    $hud.style.display = anyActive ? '' : 'none';
    const $span = document.getElementById('session-hud-balance');
    if ($span) $span.textContent = `Balance: ${_balanceSats} sats`;
  }

  // Top button label
  const $btn = document.getElementById('open-session-btn');
  if ($btn) {
    $btn.textContent = anyActive
      ? `SESSION: ${_balanceSats} ⚡`
      : '⚡ FUND SESSION';
    $btn.style.background = anyActive ? '#1a4a30' : '';
  }

  // Input bar green border + placeholder when session can send
  setInputBarSessionMode(canSend, 'Ask Timmy (session active)…');

  // Route input bar to session handler only when active (not paused)
  setSessionSendHandler(canSend ? sessionSendHandler : null);

  // Low balance notice above input bar
  const $notice = document.getElementById('low-balance-notice');
  if ($notice) $notice.style.display = lowBal ? '' : 'none';
}

function _updateActiveStep() {
  const el = document.getElementById('session-active-amount');
  if (el) {
    el.textContent = _balanceSats + ' sats';
    el.style.color = _balanceSats < MIN_BALANCE ? '#ff8844' : '#44dd88';
  }
  // Reset topup pr row visibility
  const prRow = document.getElementById('session-topup-pr-row');
  if (prRow) prRow.style.display = 'none';
}

function _clearSession() {
  _sessionId    = null;
  _macaroon     = null;
  _balanceSats  = 0;
  _sessionState = null;
  localStorage.removeItem(LS_KEY);
  setInputBarSessionMode(false);
  setSessionSendHandler(null);
  const $hud = document.getElementById('session-hud');
  if ($hud) $hud.style.display = 'none';
  const $btn = document.getElementById('open-session-btn');
  if ($btn) { $btn.textContent = '⚡ FUND SESSION'; $btn.style.background = ''; }
  const $notice = document.getElementById('low-balance-notice');
  if ($notice) $notice.style.display = 'none';
}

function _saveToStorage() {
  try {
    localStorage.setItem(LS_KEY, JSON.stringify({
      sessionId:   _sessionId,
      macaroon:    _macaroon,
      balanceSats: _balanceSats,
    }));
  } catch { /* storage unavailable */ }
}

// ── DOM helpers ───────────────────────────────────────────────────────────────

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

function _on(id, ev, fn) {
  _el(id)?.addEventListener(ev, fn);
}

function _btn(id, disabled) {
  const el = _el(id);
  if (el) el.disabled = disabled;
}

function _setSendBusy(busy) {
  const btn = document.getElementById('send-btn');
  if (btn) btn.disabled = busy;
}

function _setStep(step) {
  if (!_panel) return;
  _panel.querySelectorAll('[data-session-step]').forEach(el => {
    el.style.display = el.dataset.sessionStep === step ? '' : 'none';
  });
}

function _setStatus(step, msg, color = '#22aa66') {
  const el = document.getElementById(`session-status-${step}`);
  if (el) { el.textContent = msg; el.style.color = color; }
}

function _setError(msg) {
  const el = _el('session-error');
  if (el) el.textContent = msg;
}

function _clearError() {
  const el = _el('session-error');
  if (el) el.textContent = '';
}

function _stopPolling() {
  clearTimeout(_pollTimer);
  _pollTimer = null;
}
