import { sendVisitorMessage } from './websocket.js';
import { classify } from './edge-worker-client.js';
import { setMood, setSpeechBubble } from './agents.js';
import { getOrRefreshToken, getPubkey, disconnectNostrIdentity, showIdentityPrompt } from './nostr-identity.js';

const $fps        = document.getElementById('fps');
const $activeJobs = document.getElementById('active-jobs');
const $connStatus = document.getElementById('connection-status');
const $log        = document.getElementById('event-log');

const MAX_LOG = 6;
const logEntries = [];
let uiInitialized = false;

// ── Session-mode send override ────────────────────────────────────────────────
let _sessionSendHandler = null;

export function setSessionSendHandler(fn) {
  _sessionSendHandler = fn;
}

export function setInputBarSessionMode(active, placeholder) {
  const $input = document.getElementById('visitor-input');
  if (!$input) return;
  if (active) {
    $input.classList.add('session-active');
    $input.placeholder = placeholder || 'Ask Timmy (session active)…';
  } else {
    $input.classList.remove('session-active');
    $input.placeholder = 'Say something to Timmy…';
  }
}

// ── Model-ready indicator ─────────────────────────────────────────────────────
// A small badge on the input bar showing when local AI is warm and ready.
// Hidden until the first `ready` event from the edge worker.

let $readyBadge = null;

export function setEdgeWorkerReady() {
  if (!$readyBadge) {
    $readyBadge = document.createElement('span');
    $readyBadge.id = 'edge-ready-badge';
    $readyBadge.title = 'Local AI active — trivial queries answered without Lightning payment';
    $readyBadge.style.cssText = [
      'font-size:10px;color:#44cc88;border:1px solid #226644',
      'border-radius:3px;padding:1px 5px;margin-left:6px',
      'vertical-align:middle;cursor:default',
    ].join(';');
    $readyBadge.textContent = '⚡ local AI';
    const $input = document.getElementById('visitor-input');
    $input?.insertAdjacentElement('afterend', $readyBadge);
    // Fallback: append to send button area
    if (!$readyBadge.isConnected) {
      document.getElementById('send-btn')?.insertAdjacentElement('afterend', $readyBadge);
    }
  }
  $readyBadge.style.display = '';
}

// ── Cost preview badge ────────────────────────────────────────────────────────
// Shown beneath the input bar: "~N sats" / "FREE" / "answered locally".
// Fetched from GET /api/estimate once the user stops typing (300 ms debounce).

let _estimateTimer = null;
let $costPreview = null;

function _ensureCostPreview() {
  if ($costPreview) return $costPreview;
  $costPreview = document.getElementById('timmy-cost-preview');
  if (!$costPreview) {
    $costPreview = document.createElement('div');
    $costPreview.id = 'timmy-cost-preview';
    $costPreview.style.cssText = 'font-size:11px;color:#88aacc;margin-top:3px;min-height:14px;transition:opacity .3s;opacity:0;';
    const $input = document.getElementById('visitor-input');
    $input?.parentElement?.appendChild($costPreview);
  }
  return $costPreview;
}

function _showCostPreview(text, color = '#88aacc') {
  const el = _ensureCostPreview();
  el.textContent = text;
  el.style.color  = color;
  el.style.opacity = '1';
}

function _hideCostPreview() {
  const el = _ensureCostPreview();
  el.style.opacity = '0';
}

async function _fetchEstimate(text) {
  try {
    const token = await getOrRefreshToken('/api');
    const params = new URLSearchParams({ request: text });
    const fetchOpts = {};
    if (token) {
      fetchOpts.headers = { 'X-Nostr-Token': token };
    }

    const res = await fetch(`/api/estimate?${params}`, fetchOpts);
    if (!res.ok) return;
    const data = await res.json();

    const ft = data.identity?.free_tier;
    if (ft?.serve === 'free') {
      _showCostPreview('FREE via generosity pool', '#44dd88');
    } else if (ft?.serve === 'partial') {
      _showCostPreview(`~${ft.chargeSats} sats (${ft.absorbSats} absorbed)`, '#ffdd44');
    } else {
      const sats = data.estimatedSats ?? '?';
      _showCostPreview(`~${sats} sats estimated`, '#88aacc');
    }
  } catch {
    _hideCostPreview();
  }
}

// Fast trivial heuristic — same pattern as edge-worker.js _isGreeting().
// Prevents /api/estimate network calls for greeting messages on every keypress.
const _TRIVIAL_RE = /^(hi|hey|hello|howdy|greetings|yo|sup|hiya|what'?s up)[!?.,]?\s*$/i;

function _scheduleCostPreview(text) {
  clearTimeout(_estimateTimer);
  if (!text || text.length < 4) { _hideCostPreview(); return; }
  // Skip estimate entirely for trivially local messages — zero network calls
  if (_TRIVIAL_RE.test(text.trim())) {
    _showCostPreview('answered locally ⚡ 0 sats', '#44dd88');
    return;
  }
  _estimateTimer = setTimeout(() => _fetchEstimate(text), 300);
}

// ── Live cost ticker ──────────────────────────────────────────────────────────
// Shown in the top-right HUD during active paid interactions.
// Updated via WebSocket `cost_update` messages from the backend.

let $costTicker = null;
let _tickerHideTimer = null;

function _ensureCostTicker() {
  if ($costTicker) return $costTicker;
  $costTicker = document.getElementById('timmy-cost-ticker');
  if (!$costTicker) {
    $costTicker = document.createElement('div');
    $costTicker.id = 'timmy-cost-ticker';
    $costTicker.style.cssText = [
      'position:fixed;top:36px;right:16px',
      'font-size:11px;font-family:"Courier New",monospace',
      'color:#ffcc44;text-shadow:0 0 6px #aa8822',
      'letter-spacing:1px',
      'pointer-events:none;z-index:10',
      'transition:opacity .4s;opacity:0',
    ].join(';');
    document.body.appendChild($costTicker);
  }
  return $costTicker;
}

export function showCostTicker(sats) {
  clearTimeout(_tickerHideTimer);
  const el = _ensureCostTicker();
  el.textContent = `⚡ ~${sats} sats`;
  el.style.opacity = '1';
}

export function updateCostTicker(sats, isFinal = false) {
  clearTimeout(_tickerHideTimer);
  const el = _ensureCostTicker();
  el.textContent = isFinal ? `⚡ ${sats} sats charged` : `⚡ ~${sats} sats`;
  el.style.opacity = '1';
  if (isFinal) {
    _tickerHideTimer = setTimeout(hideCostTicker, 5000);
  }
}

export function hideCostTicker() {
  if (!$costTicker) return;
  $costTicker.style.opacity = '0';
}

// ── Nostr identity UI ─────────────────────────────────────────────────────────

let _nostrStatusEl       = null;
let _connectNostrBtn     = null;
let _disconnectNostrBtn  = null;
let _nostrPubkeyDisplay  = null;
let _getAlbyBtn          = null;

export function initNostrIdentityUI() {
  _nostrStatusEl = document.getElementById('nostr-identity-status');
  if (!_nostrStatusEl) return;

  _nostrStatusEl.innerHTML = `
    <button id="connect-nostr-btn" class="nostr-btn">⚡ Connect Nostr</button>
    <span id="nostr-pubkey-display" class="nostr-pubkey"></span>
    <button id="disconnect-nostr-btn" class="nostr-btn nostr-btn-sm">Disconnect</button>
    <button id="get-alby-btn" class="nostr-btn nostr-btn-sm">Get Alby</button>
  `;

  _connectNostrBtn    = document.getElementById('connect-nostr-btn');
  _disconnectNostrBtn = document.getElementById('disconnect-nostr-btn');
  _nostrPubkeyDisplay = document.getElementById('nostr-pubkey-display');
  _getAlbyBtn         = document.getElementById('get-alby-btn');

  if (_connectNostrBtn) {
    _connectNostrBtn.addEventListener('click', () => {
      showIdentityPrompt('/api');
    });
  }

  if (_disconnectNostrBtn) {
    _disconnectNostrBtn.addEventListener('click', () => {
      disconnectNostrIdentity();
      _updateNostrIdentityUI(null);
    });
  }

  window.addEventListener('nostr:identity-ready', e => {
    _updateNostrIdentityUI(e.detail.pubkey);
  });

  window.addEventListener('nostr:identity-disconnected', () => {
    _updateNostrIdentityUI(null);
  });

  _updateNostrIdentityUI(getPubkey());
}

function _updateNostrIdentityUI(pubkey) {
  const hasNip07 = typeof window !== 'undefined' && !!window.nostr;

  if (pubkey) {
    const formattedPubkey = pubkey.slice(0, 8) + '…' + pubkey.slice(-4);
    if (_nostrPubkeyDisplay) {
      _nostrPubkeyDisplay.textContent = `⚡ ${formattedPubkey}`;
      _nostrPubkeyDisplay.style.display = 'inline-block';
    }
    if (_connectNostrBtn)    _connectNostrBtn.style.display = 'none';
    if (_disconnectNostrBtn) _disconnectNostrBtn.style.display = 'inline-block';
    if (_getAlbyBtn)         _getAlbyBtn.style.display = 'none';
  } else {
    if (_nostrPubkeyDisplay) _nostrPubkeyDisplay.style.display = 'none';
    if (_disconnectNostrBtn) _disconnectNostrBtn.style.display = 'none';

    if (hasNip07) {
      if (_connectNostrBtn) {
        _connectNostrBtn.textContent = '⚡ Connect Nostr';
        _connectNostrBtn.style.display = 'inline-block';
      }
      if (_getAlbyBtn) _getAlbyBtn.style.display = 'none';
    } else {
      if (_connectNostrBtn) _connectNostrBtn.style.display = 'none';
      if (_getAlbyBtn) {
        _getAlbyBtn.textContent = 'Get Alby';
        _getAlbyBtn.style.display = 'inline-block';
        _getAlbyBtn.title = 'Install Alby or another NIP-07 extension to connect your Nostr identity';
        _getAlbyBtn.onclick = () => window.open('https://getalby.com/', '_blank');
      }
    }
  }
}


// ── Input bar ─────────────────────────────────────────────────────────────────

export function initUI() {
  if (uiInitialized) return;
  uiInitialized = true;
  initInputBar();
  initHeatmap();
  initNostrIdentityUI();
}

function initInputBar() {
  const $input   = document.getElementById('visitor-input');
  const $sendBtn = document.getElementById('send-btn');
  if (!$input || !$sendBtn) return;

  $input.addEventListener('input', () => _scheduleCostPreview($input.value.trim()));

  async function send() {
    const text = $input.value.trim();
    if (!text) return;
    $input.value = '';
    _hideCostPreview();

    // ── Edge triage — runs in BOTH session mode and WebSocket mode ─────────────
    // Worker returns { complexity:'trivial'|'moderate'|'complex', score, reason, localReply? }
    const cls = await classify(text);

    if (cls.complexity === 'trivial' && cls.localReply) {
      // Greeting / small-talk → answer locally, 0 sats, no network call in any mode
      appendSystemMessage(`you: ${text}`);
      setSpeechBubble(`${cls.localReply} ⚡ local`);
      _showCostPreview('answered locally ⚡ 0 sats', '#44dd88');
      setTimeout(_hideCostPreview, 3000);
      return;
    }

    // Non-trivial: delegate to session handler (if active) or WebSocket
    if (_sessionSendHandler) {
      // moderate/complex — fire estimate async for cost preview, then hand off
      if (cls.complexity === 'moderate' || cls.complexity === 'complex') {
        _fetchEstimate(text);
      }
      _sessionSendHandler(text);
      return;
    }

    // moderate or complex — fetch cost estimate (driven by complexity outcome),
    // then route to server via WebSocket.
    if (cls.complexity === 'moderate' || cls.complexity === 'complex') {
      _fetchEstimate(text);
    }

    // Route to server via WebSocket
    sendVisitorMessage(text);
    appendSystemMessage(`you: ${text}`);
  }

  $sendBtn.addEventListener('click', send);
  $input.addEventListener('keydown', e => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); }
  });
}

export function updateUI({ fps, jobCount, connectionState }) {
  if ($fps) $fps.textContent = `FPS: ${fps}`;
  if ($activeJobs) $activeJobs.textContent = `JOBS: ${jobCount}`;

  if ($connStatus) {
    if (connectionState === 'connected') {
      $connStatus.textContent = '● CONNECTED';
      $connStatus.className   = 'connected';
    } else if (connectionState === 'connecting') {
      $connStatus.textContent = '◌ CONNECTING...';
      $connStatus.className   = '';
    } else {
      $connStatus.textContent = '○ OFFLINE';
      $connStatus.className   = '';
    }
  }
}

export function appendSystemMessage(text) {
  if (!$log) return;
  const el = document.createElement('div');
  el.className = 'log-entry';
  el.textContent = text;
  logEntries.push(el);
  if (logEntries.length > MAX_LOG) {
    const removed = logEntries.shift();
    $log.removeChild(removed);
  }
  $log.appendChild(el);
  $log.scrollTop = $log.scrollHeight;
}

export function appendChatMessage(agentLabel, message, cssColor, agentId) {
  void agentLabel; void cssColor; void agentId;
  appendSystemMessage(message);
}

/**
 * Render a debate argument or verdict in the event log (#21).
 * Visually distinct from regular chat: colored by agent with a debate prefix.
 */
export function appendDebateMessage(agent, argument, isVerdict, accepted) {
  if (!$log) return;
  const el = document.createElement('div');
  el.className = 'log-entry debate-entry';
  if (isVerdict) {
    el.classList.add('debate-verdict');
    el.classList.add(accepted ? 'debate-accepted' : 'debate-rejected');
    el.textContent = `⚖ ${agent}: ${argument}`;
  } else {
    el.classList.add(agent === 'Beta-A' ? 'debate-a' : 'debate-b');
    el.textContent = `⚖ ${agent}: ${(argument || '').slice(0, 120)}`;
  }
  logEntries.push(el);
  if (logEntries.length > MAX_LOG) {
    const removed = logEntries.shift();
    $log.removeChild(removed);
  }
  $log.appendChild(el);
  $log.scrollTop = $log.scrollHeight;
}

export function loadChatHistory() { return []; }
export function saveChatHistory() {}

// ── Activity heatmap (#9) ─────────────────────────────────────────────────────
// Fetches /api/stats/activity and renders a 24-segment heatmap.
// Auto-refreshes every 5 minutes. On mobile, collapses to an icon that opens
// a full-screen overlay.

const HEATMAP_REFRESH_MS = 5 * 60 * 1000; // 5 minutes
let _heatmapTimer = null;
let _lastHours = null; // number[24] cached for overlay re-render

/** Convert an hour index (0 = oldest, 23 = current) to a UTC hour label like "3pm" or "midnight". */
function _hourLabel(hourIndex) {
  const now = new Date();
  const currentHour = now.getUTCHours();
  // slot 23 = current UTC hour, slot 0 = 23 hours ago
  const h = ((currentHour - (23 - hourIndex)) % 24 + 24) % 24;
  if (h === 0) return 'midnight';
  if (h === 12) return 'noon';
  return h < 12 ? `${h}am` : `${h - 12}pm`;
}

/** Interpolate from dim blue (#111133) to bright blue-white (#88ccff) based on 0–1 intensity. */
function _segmentColor(intensity) {
  // dim: [17, 17, 51]  bright: [136, 204, 255]
  const r = Math.round(17  + (136 - 17)  * intensity);
  const g = Math.round(17  + (204 - 17)  * intensity);
  const b = Math.round(51  + (255 - 51)  * intensity);
  return `rgb(${r},${g},${b})`;
}

function _renderSegments(hours, container, isMobile) {
  container.innerHTML = '';
  const max = Math.max(...hours, 1); // avoid div-by-zero
  const currentSlot = 23;

  hours.forEach((count, i) => {
    const seg = document.createElement('div');
    seg.className = 'hm-seg' + (i === currentSlot ? ' hm-seg-current' : '');
    const intensity = count / max;
    const color = _segmentColor(intensity);
    seg.style.background = color;
    if (i === currentSlot) seg.style.color = color; // used by pulse animation
    seg.dataset.index = String(i);
    seg.dataset.count = String(count);
    if (isMobile) {
      seg.style.width = '14px';
      seg.style.height = '28px';
    }
    container.appendChild(seg);
  });
}

function _initHeatmapTooltip(barEl) {
  const $tip = document.getElementById('heatmap-tooltip');
  if (!$tip) return;

  barEl.addEventListener('mousemove', e => {
    const seg = e.target.closest('.hm-seg');
    if (!seg) { $tip.style.display = 'none'; return; }
    const i = Number(seg.dataset.index);
    const count = Number(seg.dataset.count);
    const label = _hourLabel(i);
    $tip.textContent = `${label}: ${count} job${count !== 1 ? 's' : ''} submitted`;
    $tip.style.display = 'block';
    $tip.style.left = `${e.clientX + 10}px`;
    $tip.style.top  = `${e.clientY - 24}px`;
  });

  barEl.addEventListener('mouseleave', () => { $tip.style.display = 'none'; });
}

async function _fetchAndRenderHeatmap() {
  try {
    const res = await fetch('/api/stats/activity');
    if (!res.ok) return;
    const data = await res.json();
    const hours = Array.isArray(data.hours) ? data.hours : [];
    if (hours.length !== 24) return;
    _lastHours = hours;

    const $bar = document.getElementById('heatmap-bar');
    if ($bar) _renderSegments(hours, $bar, false);

    const $overlayBar = document.getElementById('heatmap-overlay-bar');
    if ($overlayBar) _renderSegments(hours, $overlayBar, true);
  } catch {
    // silently ignore fetch errors
  }
}

export function initHeatmap() {
  const $bar      = document.getElementById('heatmap-bar');
  const $iconBtn  = document.getElementById('heatmap-icon-btn');
  const $overlay  = document.getElementById('heatmap-overlay');
  const $closeBtn = document.getElementById('heatmap-overlay-close');

  if ($bar) _initHeatmapTooltip($bar);

  if ($iconBtn && $overlay) {
    $iconBtn.addEventListener('click', () => {
      $overlay.classList.add('open');
      if (_lastHours) {
        const $overlayBar = document.getElementById('heatmap-overlay-bar');
        if ($overlayBar) _renderSegments(_lastHours, $overlayBar, true);
      }
    });
  }

  if ($closeBtn && $overlay) {
    $closeBtn.addEventListener('click', () => $overlay.classList.remove('open'));
  }

  // Initial fetch then schedule refresh
  void _fetchAndRenderHeatmap();
  _heatmapTimer = setInterval(_fetchAndRenderHeatmap, HEATMAP_REFRESH_MS);
}
