import { html, nothing } from 'lit';
import { LightElement } from '../lib/base.js';

const PAGE_ID = 'session';

function formatDate(iso) {
  if (!iso) return '—';
  return new Date(iso).toLocaleString(undefined, {
    day: '2-digit', month: '2-digit', year: '2-digit',
    hour: '2-digit', minute: '2-digit', second: '2-digit',
  });
}

function formatTime(iso) {
  if (!iso) return null;
  return new Date(iso).toLocaleTimeString('en-GB', {
    hour: '2-digit', minute: '2-digit', second: '2-digit',
  });
}

function sourceBadgeClass(source) {
  const map = { tic: 'bg-warning text-dark', cron: 'bg-info text-dark', web: 'bg-primary', telegram: 'bg-success', mobile: 'bg-secondary' };
  return map[source] ?? 'bg-secondary';
}

function jsonPretty(val) {
  if (val == null) return '—';
  if (typeof val === 'string') return val;
  return JSON.stringify(val, null, 2);
}

export class SessionDetailPage extends LightElement {
  static properties = {
    _open:            { state: true },
    _sessionId:       { state: true },
    _data:            { state: true },
    _loading:         { state: true },
    _error:           { state: true },
    _live:            { state: true },
    _expandedTools:   { state: true },
    _expandedReasons: { state: true },
  };

  constructor() {
    super();
    this._open            = false;
    this._sessionId       = null;
    this._data            = null;
    this._loading         = false;
    this._error           = null;
    this._live            = false;
    this._expandedTools   = new Set();
    this._expandedReasons = new Set();
    this._ws              = null;
    this._wsReconnectTimer = null;
  }

  connectedCallback() {
    super.connectedCallback();
    window.addEventListener('llm-page-change', (e) => {
      this._open = e.detail.page === PAGE_ID;
      this.style.display = this._open ? 'flex' : 'none';
      if (this._open) this._loadFromHash();
      else            this._closeWs();
    });
    window.addEventListener('hashchange', () => {
      if (this._open) this._loadFromHash();
    });
  }

  disconnectedCallback() {
    super.disconnectedCallback();
    this._closeWs();
  }

  _idFromHash() {
    const parts = location.hash.replace('#', '').split('/');
    if (parts[0] === PAGE_ID && parts[1]) return parseInt(parts[1], 10);
    return null;
  }

  _loadFromHash() {
    const id = this._idFromHash();
    if (id != null && id !== this._sessionId) {
      this._sessionId = id;
      this._closeWs();
      this._fetch(id);
    }
  }

  async _fetch(id) {
    this._loading = true;
    this._error   = null;
    this._data    = null;
    this._expandedTools   = new Set();
    this._expandedReasons = new Set();
    try {
      const res = await fetch(`/api/sessions/${id}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      this._data = await res.json();
      this._connectWs(id);
    } catch (e) {
      this._error = e.message;
    } finally {
      this._loading = false;
    }
  }

  // ── Live WebSocket ─────────────────────────────────────────────────────────

  _connectWs(id) {
    this._closeWs();
    const proto = location.protocol === 'https:' ? 'wss' : 'ws';
    const ws = new WebSocket(`${proto}://${location.host}/api/ws/session/${id}`);
    this._ws = ws;

    ws.onopen = () => { this._live = true; };

    ws.onmessage = (e) => {
      try { this._handleEvent(JSON.parse(e.data)); } catch {}
    };

    ws.onclose = () => {
      this._live = false;
      this._ws = null;
      // Reconnect after 3 s if the page is still open and showing this session.
      if (this._open && this._sessionId === id) {
        this._wsReconnectTimer = setTimeout(() => this._connectWs(id), 3000);
      }
    };

    ws.onerror = () => ws.close();
  }

  _closeWs() {
    clearTimeout(this._wsReconnectTimer);
    if (this._ws) { this._ws.onclose = null; this._ws.close(); this._ws = null; }
    this._live = false;
  }

  _handleEvent(ev) {
    if (!this._data) return;
    const msgs = [...this._data.messages];
    const now  = new Date().toISOString();

    switch (ev.type) {
      case 'tool_start':
        msgs.push({
          kind:         'tool',
          tool_call_id: ev.tool_call_id,
          message_id:   ev.message_id,
          name:         ev.name,
          label_short:  ev.label_short,
          label_full:   ev.label_full,
          arguments:    ev.arguments,
          result:       null,
          error:        null,
          status:       'pending',
          created_at:   now,
        });
        break;

      case 'tool_done': {
        const i = msgs.findIndex(m => m.kind === 'tool' && m.tool_call_id === ev.tool_call_id);
        if (i >= 0) msgs[i] = { ...msgs[i], result: ev.result, status: 'done' };
        break;
      }

      case 'tool_error': {
        const i = msgs.findIndex(m => m.kind === 'tool' && m.tool_call_id === ev.tool_call_id);
        if (i >= 0) msgs[i] = { ...msgs[i], error: ev.error, status: 'error' };
        break;
      }

      case 'thinking':
        msgs.push({
          kind:         'thinking',
          message_id:   ev.message_id,
          content:      ev.content,
          reasoning:    '',
          input_tokens:  ev.input_tokens  ?? null,
          output_tokens: ev.output_tokens ?? null,
          created_at:   now,
        });
        break;

      case 'done':
        msgs.push({
          kind:         'assistant',
          message_id:   ev.message_id,
          content:      ev.content,
          reasoning:    '',
          input_tokens:  ev.input_tokens  ?? null,
          output_tokens: ev.output_tokens ?? null,
          created_at:   now,
        });
        break;

      case 'user_message':
        msgs.push({
          kind:         'user',
          content:      ev.content,
          is_synthetic: false,
          created_at:   now,
        });
        break;

      case 'agent_start':
        msgs.push({
          kind:     'agent',
          agent_id: ev.agent_id,
          depth:    ev.depth,
        });
        break;

      case 'agent_done':
        msgs.push({
          kind:     'agent_end',
          agent_id: ev.agent_id,
        });
        break;

      default:
        return; // ignore unknown events
    }

    this._data = { ...this._data, messages: msgs };
  }

  _toggleTool(id) {
    const next = new Set(this._expandedTools);
    next.has(id) ? next.delete(id) : next.add(id);
    this._expandedTools = next;
  }

  _toggleReason(id) {
    const next = new Set(this._expandedReasons);
    next.has(id) ? next.delete(id) : next.add(id);
    this._expandedReasons = next;
  }

  // ── Renderers ─────────────────────────────────────────────────────────────────

  _back() {
    history.back();
  }

  _renderSessionHeader(session) {
    return html`
      <div class="sd-session-header">
        <div class="d-flex align-items-center gap-2 flex-wrap">
          <button class="btn btn-sm btn-outline-secondary sd-back-btn" @click=${() => this._back()}>
            <i class="bi bi-arrow-left"></i> Back
          </button>
          <span class="badge ${sourceBadgeClass(session.source)}">${session.source}</span>
          <span class="fw-semibold font-monospace">agent: ${session.agent_id}</span>
          <span class="text-secondary small">id: ${session.id}</span>
          ${session.is_ephemeral ? html`<span class="badge bg-light text-dark border">ephemeral</span>` : nothing}
          ${!session.is_interactive ? html`<span class="badge bg-light text-dark border">automated</span>` : nothing}
          ${this._live
            ? html`<span class="sd-live-badge"><span class="sd-live-dot"></span>live</span>`
            : nothing}
        </div>
        <div class="text-secondary small mt-1">${formatDate(session.created_at)}</div>
      </div>
    `;
  }

  _renderUserMsg(item, idx) {
    const time = formatTime(item.created_at);
    return html`
      <div class="sd-msg sd-msg--user ${item.is_synthetic ? 'sd-msg--synthetic' : ''}">
        <div class="sd-msg-role">
          ${item.is_synthetic
            ? html`<span class="badge bg-warning text-dark me-1" style="font-size:0.65rem">synthetic</span>`
            : nothing}
          <span>User</span>
          ${time ? html`<span class="sd-msg-time">${time}</span>` : nothing}
        </div>
        <div class="sd-msg-content">${item.content}</div>
        ${item.failed ? html`<div class="sd-msg-failed">failed</div>` : nothing}
      </div>
    `;
  }

  _renderAssistantMsg(item, idx) {
    const key = `ast-${idx}`;
    const hasReasoning = item.reasoning && item.reasoning.trim().length > 0;
    const expanded = this._expandedReasons.has(key);
    const time = formatTime(item.created_at);
    return html`
      <div class="sd-msg sd-msg--assistant ${item.failed ? 'sd-msg--failed' : ''}">
        <div class="sd-msg-role">
          Assistant
          ${time ? html`<span class="sd-msg-time">${time}</span>` : nothing}
          ${item.input_tokens != null ? html`<span class="sd-tokens">${item.input_tokens}↑ ${item.output_tokens}↓</span>` : nothing}
        </div>
        ${hasReasoning ? html`
          <div class="sd-reasoning-toggle" @click=${() => this._toggleReason(key)}>
            <i class="bi bi-brain me-1"></i>
            Reasoning
            <i class="bi bi-chevron-${expanded ? 'up' : 'down'} ms-1"></i>
          </div>
          ${expanded ? html`<pre class="sd-reasoning-block">${item.reasoning}</pre>` : nothing}
        ` : nothing}
        ${item.content ? html`<div class="sd-msg-content">${item.content}</div>` : nothing}
      </div>
    `;
  }

  _renderThinkingMsg(item, idx) {
    const key = `think-${item.message_id ?? idx}`;
    const hasReasoning = item.reasoning && item.reasoning.trim().length > 0;
    const expanded = this._expandedReasons.has(key);
    const time = formatTime(item.created_at);
    return html`
      <div class="sd-msg sd-msg--thinking ${item.failed ? 'sd-msg--failed' : ''}">
        <div class="sd-msg-role">
          <i class="bi bi-lightning-charge me-1"></i>Thinking
          ${time ? html`<span class="sd-msg-time">${time}</span>` : nothing}
          ${item.input_tokens != null ? html`<span class="sd-tokens">${item.input_tokens}↑ ${item.output_tokens}↓</span>` : nothing}
        </div>
        ${hasReasoning ? html`
          <div class="sd-reasoning-toggle" @click=${() => this._toggleReason(key)}>
            <i class="bi bi-brain me-1"></i>
            Reasoning
            <i class="bi bi-chevron-${expanded ? 'up' : 'down'} ms-1"></i>
          </div>
          ${expanded ? html`<pre class="sd-reasoning-block">${item.reasoning}</pre>` : nothing}
        ` : nothing}
        ${item.content ? html`<div class="sd-msg-content">${item.content}</div>` : nothing}
      </div>
    `;
  }

  _renderToolMsg(item, idx) {
    const key = item.tool_call_id ?? idx;
    const expanded = this._expandedTools.has(key);
    const statusClass = { done: 'sd-tool--done', error: 'sd-tool--error', pending: 'sd-tool--pending' }[item.status] ?? '';
    return html`
      <div class="sd-tool ${statusClass}">
        <div class="sd-tool-header" @click=${() => this._toggleTool(key)}>
          <span class="sd-tool-icon">
            <i class="bi bi-${item.status === 'done' ? 'check-circle' : item.status === 'error' ? 'x-circle' : 'hourglass-split'}"></i>
          </span>
          <span class="sd-tool-name">${item.label_short ?? item.name}</span>
          <i class="bi bi-chevron-${expanded ? 'up' : 'down'} ms-auto"></i>
        </div>
        ${expanded ? html`
          <div class="sd-tool-body">
            ${item.label_full && item.label_full !== item.label_short
              ? html`<div class="sd-tool-label-full text-secondary small mb-2">${item.label_full}</div>`
              : nothing}
            <div class="sd-tool-section-label">Arguments</div>
            <pre class="sd-code-block">${jsonPretty(item.arguments)}</pre>
            <div class="sd-tool-section-label mt-2">
              ${item.status === 'error' ? 'Error' : 'Result'}
            </div>
            <pre class="sd-code-block ${item.status === 'error' ? 'sd-code-block--error' : ''}">${
              item.result ?? item.error ?? '—'
            }</pre>
          </div>
        ` : nothing}
      </div>
    `;
  }

  _renderAgentFrame(item) {
    return html`
      <div class="sd-agent-frame-start">
        <i class="bi bi-robot me-1"></i>
        <span>Sub-agent: <strong>${item.agent_id}</strong></span>
        <span class="text-secondary small ms-2">depth ${item.depth}</span>
      </div>
    `;
  }

  _renderAgentFrameEnd(item) {
    return html`<div class="sd-agent-frame-end">end of ${item.agent_id}</div>`;
  }

  _renderMessage(item, idx) {
    switch (item.kind) {
      case 'user':      return this._renderUserMsg(item, idx);
      case 'assistant': return this._renderAssistantMsg(item, idx);
      case 'thinking':  return this._renderThinkingMsg(item, idx);
      case 'tool':      return this._renderToolMsg(item, idx);
      case 'agent':     return this._renderAgentFrame(item);
      case 'agent_end': return this._renderAgentFrameEnd(item);
      default:          return nothing;
    }
  }

  render() {
    return html`
      <style>
        .sd-container {
          display: flex;
          flex-direction: column;
          flex: 1;
          min-height: 0;
          overflow-y: auto;
          padding: 1.5rem;
          gap: 0;
        }
        .sd-back-btn {
          font-size: 0.8rem;
          padding: 0.2rem 0.6rem;
        }
        .sd-session-header {
          background: var(--bs-tertiary-bg);
          border: 1px solid var(--bs-border-color);
          border-radius: 0.5rem;
          padding: 0.875rem 1rem;
          margin-bottom: 1.25rem;
        }
        .sd-msg {
          border-left: 3px solid transparent;
          padding: 0.625rem 0.875rem;
          margin-bottom: 0.5rem;
          border-radius: 0 0.375rem 0.375rem 0;
          background: var(--bs-body-bg);
        }
        .sd-msg--user        { border-left-color: var(--bs-primary); background: var(--bs-tertiary-bg); }
        .sd-msg--synthetic   { opacity: 0.75; border-left-color: var(--bs-warning); }
        .sd-msg--assistant   { border-left-color: var(--bs-success); }
        .sd-msg--thinking    { border-left-color: var(--bs-info); background: var(--bs-tertiary-bg); }
        .sd-msg--failed      { border-left-color: var(--bs-danger) !important; opacity: 0.7; }
        .sd-msg-role {
          font-size: 0.7rem;
          font-weight: 600;
          text-transform: uppercase;
          letter-spacing: 0.05em;
          color: var(--bs-secondary-color);
          margin-bottom: 0.25rem;
          display: flex;
          align-items: center;
          gap: 0.375rem;
        }
        .sd-msg-time {
          font-weight: 400;
          font-size: 0.65rem;
          color: var(--bs-secondary-color);
          font-family: var(--bs-font-monospace);
        }
        .sd-tokens {
          font-weight: 400;
          font-size: 0.65rem;
          color: var(--bs-secondary-color);
          margin-left: auto;
        }
        .sd-msg-content {
          white-space: pre-wrap;
          word-break: break-word;
          font-size: 0.875rem;
          line-height: 1.55;
        }
        .sd-msg-failed { font-size: 0.7rem; color: var(--bs-danger); margin-top: 0.25rem; }
        .sd-reasoning-toggle {
          display: inline-flex;
          align-items: center;
          cursor: pointer;
          font-size: 0.7rem;
          color: var(--bs-secondary-color);
          border: 1px solid var(--bs-border-color);
          border-radius: 999px;
          padding: 0.125rem 0.5rem;
          margin-bottom: 0.375rem;
          user-select: none;
        }
        .sd-reasoning-toggle:hover { background: var(--bs-tertiary-bg); }
        .sd-reasoning-block {
          background: var(--bs-tertiary-bg);
          border: 1px solid var(--bs-border-color);
          border-radius: 0.375rem;
          padding: 0.625rem 0.75rem;
          font-size: 0.78rem;
          white-space: pre-wrap;
          word-break: break-word;
          margin-bottom: 0.5rem;
          max-height: 20rem;
          overflow-y: auto;
          color: var(--bs-secondary-color);
        }
        .sd-tool {
          border: 1px solid var(--bs-border-color);
          border-radius: 0.375rem;
          margin-bottom: 0.375rem;
        }
        .sd-tool--done    { border-left: 3px solid var(--bs-success); }
        .sd-tool--error   { border-left: 3px solid var(--bs-danger); }
        .sd-tool--pending { border-left: 3px solid var(--bs-warning); }
        .sd-tool-header {
          display: flex;
          align-items: center;
          gap: 0.5rem;
          padding: 0.5rem 0.75rem;
          cursor: pointer;
          user-select: none;
          font-size: 0.82rem;
          font-weight: 500;
        }
        .sd-tool-header:hover { background: var(--bs-tertiary-bg); }
        .sd-tool-icon { color: var(--bs-secondary-color); }
        .sd-tool-name { font-family: var(--bs-font-monospace); font-size: 0.8rem; }
        .sd-tool-body {
          padding: 0.625rem 0.75rem;
          border-top: 1px solid var(--bs-border-color);
          background: var(--bs-body-bg);
        }
        .sd-tool-section-label {
          font-size: 0.68rem;
          font-weight: 600;
          text-transform: uppercase;
          letter-spacing: 0.05em;
          color: var(--bs-secondary-color);
          margin-bottom: 0.25rem;
        }
        .sd-tool-label-full { font-style: italic; }
        .sd-code-block {
          background: var(--bs-tertiary-bg);
          border: 1px solid var(--bs-border-color);
          border-radius: 0.25rem;
          padding: 0.5rem 0.625rem;
          font-size: 0.75rem;
          white-space: pre-wrap;
          word-break: break-word;
          max-height: 16rem;
          overflow-y: auto;
          margin: 0;
        }
        .sd-code-block--error { border-color: var(--bs-danger); color: var(--bs-danger); }
        .sd-agent-frame-start {
          display: flex;
          align-items: center;
          gap: 0.25rem;
          font-size: 0.78rem;
          color: var(--bs-secondary-color);
          border-top: 1px dashed var(--bs-border-color);
          padding: 0.375rem 0;
          margin: 0.25rem 0 0.25rem 1rem;
        }
        .sd-agent-frame-end {
          font-size: 0.72rem;
          color: var(--bs-secondary-color);
          border-bottom: 1px dashed var(--bs-border-color);
          padding: 0.25rem 0;
          margin: 0 0 0.375rem 1rem;
        }
        .sd-live-badge {
          display: inline-flex;
          align-items: center;
          gap: 0.3rem;
          font-size: 0.65rem;
          font-weight: 600;
          text-transform: uppercase;
          letter-spacing: 0.05em;
          color: var(--bs-success);
          border: 1px solid var(--bs-success);
          border-radius: 999px;
          padding: 0.1rem 0.45rem;
        }
        .sd-live-dot {
          width: 6px;
          height: 6px;
          border-radius: 50%;
          background: var(--bs-success);
          animation: sd-pulse 1.4s ease-in-out infinite;
        }
        @keyframes sd-pulse {
          0%, 100% { opacity: 1; }
          50%       { opacity: 0.25; }
        }
      </style>

      <div class="sd-container">
        ${this._loading ? html`
          <div class="text-center text-secondary py-5">
            <div class="spinner-border spinner-border-sm me-2"></div>Loading session…
          </div>
        ` : this._error ? html`
          <div class="alert alert-danger">${this._error}</div>
        ` : !this._data ? html`
          <div class="text-secondary text-center py-5">No session loaded.<br>
            <span class="small">Navigate to <code>#session/{id}</code> to view a session.</span>
          </div>
        ` : html`
          ${this._renderSessionHeader(this._data.session)}
          ${this._data.messages.length === 0
            ? html`<div class="text-secondary text-center py-4">No messages in this session.</div>`
            : this._data.messages.map((m, i) => this._renderMessage(m, i))
          }
        `}
      </div>
    `;
  }
}
