import { SRC, SRC_DEFAULT, MSG } from './constants.js';
import { escapeHtml, formatDate, timeToMinutes, detectOverlaps, assignLanes } from './utils.js';
import { getData, loadSessionMessages, getMsgCache } from './data.js';
import { renderSessionHeader, renderMessagesHtml, renderContent, makeCollapsible, isCompaction, isInterruption } from './render.js';
import { renderMinimap, updateViewport, hideMinimap } from './minimap.js';

let ganttMeta = [];
let currentSessionId = null;
let currentSessionSource = null;
let userMsgEls = [];
let lastCompactJump = 0;
let lastInterruptJump = 0;

export function getCurrentSessionId() { return currentSessionId; }

export function getFilteredSessions(selectedDate) {
  const DATA = getData();
  const name = document.getElementById('dev-select').value;
  if (!name || !DATA.developers[name]) return [];
  let sessions = DATA.developers[name].sessions;
  const cli = document.getElementById('cli-select').value;
  if (cli) sessions = sessions.filter(s => s.source === cli);
  if (selectedDate) sessions = sessions.filter(s => s.start.slice(0, 10) === selectedDate);
  return sessions;
}

export function renderSessions(selectedDate) {
  const list = document.getElementById('session-list');
  const stats = document.getElementById('stats');
  const DATA = getData();
  const name = document.getElementById('dev-select').value;
  if (!name || !DATA.developers[name]) {
    list.innerHTML = '<div class="text-center py-20 text-gray-300 text-sm">Select a developer</div>';
    stats.textContent = ''; return;
  }
  const sessions = getFilteredSessions(selectedDate);
  const sessionsWithCompactions = sessions.filter(s => s.compactions > 0).length;
  const sessionsWithInterruptions = sessions.filter(s => s.interruptions > 0).length;
  stats.innerHTML = `${sessions.length} sessions`
    + (sessionsWithCompactions ? ` &middot; <span class="text-amber-500">&#9889; ${sessionsWithCompactions} compacted</span>` : '')
    + (sessionsWithInterruptions ? ` &middot; <span class="text-red-500"><span class="inline-block w-1.5 h-1.5 rounded-full bg-red-500 align-middle"></span> ${sessionsWithInterruptions} interrupted</span>` : '');

  const byDate = {};
  for (const s of sessions) { const d = s.start.slice(0, 10); if (!byDate[d]) byDate[d] = []; byDate[d].push(s); }

  ganttMeta = [];
  let ganttIdx = 0;
  let html = '';
  for (const date of Object.keys(byDate).sort().reverse()) {
    const daySessions = byDate[date];
    const hasParallel = detectOverlaps(daySessions);
    const { assignments, laneCount } = assignLanes(daySessions);

    let minH = 24, maxH = 0;
    for (const s of daySessions) {
      const sh = parseInt(s.start.slice(11, 13));
      const eh = s.end.slice(0, 10) !== date ? 24 : parseInt(s.end.slice(11, 13)) + 1;
      minH = Math.min(minH, sh);
      maxH = Math.max(maxH, eh);
    }
    minH = Math.max(0, minH);
    maxH = Math.min(24, maxH);
    const hourSpan = maxH - minH || 1;

    html += `<div class="mb-5">
      <div class="flex items-center gap-2 mb-2 px-1">
        <span class="text-[11px] font-semibold text-gray-400 uppercase tracking-wider">${formatDate(date)}</span>
        <span class="text-[10px] text-gray-300">${daySessions.length} sessions</span>
        ${hasParallel ? '<span class="parallel-badge">PARALLEL</span>' : ''}
      </div>`;

    const gIdx = ganttIdx++;
    ganttMeta.push({ date, minH, hourSpan, sessions: daySessions });

    html += `<div class="gantt bg-white rounded-lg border border-gray-100 p-2 mb-2">`;
    html += `<div class="gantt-hours">`;
    for (let h = minH; h < maxH; h++) {
      html += `<div class="gantt-hour">${String(h).padStart(2, '0')}</div>`;
    }
    html += `</div>`;
    html += `<div class="gantt-area" data-gidx="${gIdx}">`;
    html += `<div class="gantt-cursor" id="gantt-cursor-${gIdx}"><div class="gantt-cursor-label" id="gantt-cursor-label-${gIdx}"></div></div>`;
    for (let lane = 0; lane < laneCount; lane++) {
      html += `<div class="gantt-lane">`;
      for (const s of daySessions) {
        if (assignments.get(s.id) !== lane) continue;
        const startMin = timeToMinutes(s.start);
        const endWraps = s.end.slice(0, 10) !== date;
        const endMin = endWraps ? 24 * 60 : timeToMinutes(s.end);
        const left = ((startMin - minH * 60) / (hourSpan * 60)) * 100;
        const width = Math.max(1.5, ((endMin - startMin) / (hourSpan * 60)) * 100);
        const src = SRC[s.source] || SRC_DEFAULT;
        const dur = Math.round((endMin - startMin));
        const durLabel = dur < 60 ? `${dur}m` : `${Math.floor(dur / 60)}h${dur % 60 ? dur % 60 + 'm' : ''}`;
        const repoShort = s.repo_name ? String(s.repo_name).split('/').pop() : '';
        const label = width > 12 ? `${s.start.slice(11, 16)}–${endWraps ? '00:00+' : s.end.slice(11, 16)} ${durLabel}${repoShort ? ' · ' + repoShort : ''}` : (width > 5 ? durLabel : '');
        const compactIcon = s.compactions ? `<span class="text-[8px] ml-auto opacity-75">&#9889;${s.compactions}</span>` : '';
        const interruptIcon = s.interruptions ? `<span class="text-[8px] opacity-75"><span class="inline-block w-1 h-1 rounded-full bg-red-300 align-middle"></span>${s.interruptions}</span>` : '';
        html += `<div class="gantt-bar gantt-bar-${s.source}" style="left:${left}%;width:${width}%;" data-sid="${s.id}" data-source="${s.source}" title="${s.start.slice(11, 16)}–${s.end.slice(11, 16)} ${durLabel} ${src.label}${repoShort ? ' · ' + repoShort : ''}${s.compactions ? ' · ' + s.compactions + ' compaction(s)' : ''}${s.interruptions ? ' · ' + s.interruptions + ' interruption(s)' : ''}">${label}${width > 8 ? compactIcon + interruptIcon : ''}</div>`;
      }
      html += `</div>`;
    }
    html += `</div></div>`;

    html += `<div class="timeline">`;
    for (const s of daySessions) {
      const src = SRC[s.source] || SRC_DEFAULT;
      const startTime = s.start.slice(11, 16);
      const endTime = s.end.slice(11, 16);
      const dur = Math.round((new Date(s.end) - new Date(s.start)) / 60000);
      const durLabel = dur < 60 ? `${dur}m` : `${Math.floor(dur / 60)}h ${dur % 60}m`;
      html += `
        <div class="tl-node" data-sid="${s.id}" data-source="${s.source}">
          <div class="tl-dot ${src.dot}${s.compactions ? ' tl-dot-compacted' : ''}${s.interruptions ? ' tl-dot-interrupted' : ''}"></div>
          <div class="flex items-center gap-2 mb-0.5">
            <span class="text-sm font-semibold text-gray-800">${startTime}</span>
            <span class="text-[10px] text-gray-300">&rarr;</span>
            <span class="text-sm text-gray-500">${endTime}</span>
            <span class="text-[10px] text-gray-400">${durLabel}</span>
          </div>
          <div class="flex items-center gap-1.5 flex-wrap">
            <span class="text-[10px] font-medium px-1.5 py-0.5 rounded ${src.badge}">${src.label}</span>
            <span class="text-[10px] text-gray-400">${s.user_messages} prompts &middot; ${s.tool_calls} tools</span>
            ${s.compactions ? `<span class="compaction-badge">&#9889; ${s.compactions}</span>` : ''}
            ${s.interruptions ? `<span class="interruption-badge"><span class="inline-block w-1.5 h-1.5 rounded-full bg-red-500"></span> ${s.interruptions}</span>` : ''}
          </div>
          ${s.repo_name ? `<div class="text-[10px] text-gray-400 mt-0.5 truncate">${escapeHtml(String(s.repo_name))}</div>` : ''}
        </div>`;
    }
    html += '</div></div>';
  }
  list.innerHTML = html || '<div class="text-center py-20 text-gray-300 text-sm">No sessions match filters</div>';
}

export async function showMessages(sessionId, source, card) {
  document.querySelectorAll('.tl-node').forEach(c => c.classList.remove('active'));
  document.querySelectorAll('.gantt-bar').forEach(c => c.classList.remove('active'));
  document.querySelectorAll(`[data-sid="${sessionId}"]`).forEach(c => c.classList.add('active'));
  if (card) card.classList.add('active');

  currentSessionId = sessionId;
  currentSessionSource = source;
  const panel = document.getElementById('message-panel');
  const header = document.getElementById('session-header');

  header.classList.add('hidden');
  panel.innerHTML = '<div class="flex items-center justify-center h-full gap-2 text-gray-400 text-sm"><span class="spinner"></span> Loading session...</div>';

  const msgs = await loadSessionMessages(sessionId);
  if (!msgs.length) { panel.innerHTML = '<div class="flex items-center justify-center h-full text-gray-300 text-sm">No messages</div>'; return; }

  header.innerHTML = renderSessionHeader(msgs, source, sessionId, lastCompactJump);
  header.classList.remove('hidden');
  panel.innerHTML = renderMessagesHtml(msgs, source, sessionId);
  panel.scrollTop = 0;
  onSessionRendered();
  // Render minimap after layout settles
  requestAnimationFrame(() => renderMinimap(msgs));
}

function onSessionRendered() {
  const panel = document.getElementById('message-panel');
  userMsgEls = [...panel.querySelectorAll('[data-msgtype="user"]')];
  const jumpBar = document.getElementById('jump-bar');
  const jumpPos = document.getElementById('jump-pos');
  jumpBar.classList.toggle('hidden', userMsgEls.length < 3);
  if (userMsgEls.length) jumpPos.textContent = `prompt 1/${userMsgEls.length}`;
}

export function jumpMsg(dir) {
  const panel = document.getElementById('message-panel');
  if (!userMsgEls.length) return;
  let current = 0;
  for (let i = 0; i < userMsgEls.length; i++) {
    if (userMsgEls[i].offsetTop <= panel.scrollTop + 60) current = i;
  }
  const next = Math.max(0, Math.min(userMsgEls.length - 1, current + dir));
  userMsgEls[next].scrollIntoView({ behavior: 'smooth', block: 'start' });
}

export function jumpCompact(idx) {
  const panel = document.getElementById('message-panel');
  const breaks = panel.querySelectorAll('.compaction-break');
  if (!breaks.length) return;
  const target = breaks[idx % breaks.length];
  target.scrollIntoView({ behavior: 'smooth', block: 'center' });
  target.querySelector('.compaction-badge').style.animation = 'none';
  target.querySelector('.compaction-badge').offsetHeight;
  target.querySelector('.compaction-badge').style.animation = 'compact-pulse 0.6s ease-out';
  lastCompactJump = (idx + 1) % breaks.length;
}

export function getLastCompactJump() { return lastCompactJump; }

export function jumpInterrupt(idx) {
  const panel = document.getElementById('message-panel');
  const breaks = panel.querySelectorAll('.interruption-break');
  if (!breaks.length) return;
  const target = breaks[idx % breaks.length];
  target.scrollIntoView({ behavior: 'smooth', block: 'center' });
  target.querySelector('.interruption-badge').style.animation = 'none';
  target.querySelector('.interruption-badge').offsetHeight;
  target.querySelector('.interruption-badge').style.animation = 'interrupt-pulse 0.6s ease-out';
  lastInterruptJump = (idx + 1) % breaks.length;
}

export function getLastInterruptJump() { return lastInterruptJump; }

export function splitJump(paneId, dir) {
  const pane = document.getElementById(paneId);
  if (!pane) return;
  const userEls = [...pane.querySelectorAll('[data-msgtype="user"]')];
  if (!userEls.length) return;
  const headerH = pane.querySelector('.split-header')?.offsetHeight || 0;
  let current = 0;
  for (let i = 0; i < userEls.length; i++) {
    if (userEls[i].offsetTop <= pane.scrollTop + headerH + 20) current = i;
  }
  const next = Math.max(0, Math.min(userEls.length - 1, current + dir));
  userEls[next].scrollIntoView({ behavior: 'smooth', block: 'start' });
  const posEl = pane.querySelector('.split-jump-pos');
  if (posEl) posEl.textContent = `prompt ${next + 1}/${userEls.length}`;
}

// Gantt interactions
function ganttPctToTime(pct, gIdx) {
  const g = ganttMeta[gIdx];
  const totalMin = g.minH * 60 + pct * g.hourSpan * 60;
  const h = Math.floor(totalMin / 60);
  const m = Math.floor(totalMin % 60);
  return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`;
}

export function ganttHover(e, gIdx, area) {
  const rect = area.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const pct = x / rect.width;
  const cursor = document.getElementById('gantt-cursor-' + gIdx);
  const label = document.getElementById('gantt-cursor-label-' + gIdx);
  cursor.style.left = x + 'px';
  label.textContent = ganttPctToTime(pct, gIdx);
}

export function ganttClick(e, gIdx, area) {
  const rect = area.getBoundingClientRect();
  const pct = (e.clientX - rect.left) / rect.width;
  const g = ganttMeta[gIdx];
  const clickMin = g.minH * 60 + pct * g.hourSpan * 60;
  const clickTime = ganttPctToTime(pct, gIdx);
  const clickDate = g.date;

  const activeSessions = g.sessions.filter(s => {
    const sMin = timeToMinutes(s.start);
    const eWraps = s.end.slice(0, 10) !== clickDate;
    const eMin = eWraps ? 24 * 60 : timeToMinutes(s.end);
    return clickMin >= sMin && clickMin <= eMin;
  });

  if (activeSessions.length === 0) {
    let closest = null, closestDist = Infinity;
    for (const s of g.sessions) {
      const sMin = timeToMinutes(s.start);
      const eWraps = s.end.slice(0, 10) !== clickDate;
      const eMin = eWraps ? 24 * 60 : timeToMinutes(s.end);
      const dist = Math.min(Math.abs(clickMin - sMin), Math.abs(clickMin - eMin));
      if (dist < closestDist) { closestDist = dist; closest = s; }
    }
    if (closest) showMessages(closest.id, closest.source, null);
    return;
  }

  if (activeSessions.length === 1) {
    showMessages(activeSessions[0].id, activeSessions[0].source, null);
    return;
  }

  showParallelAtTime(activeSessions, clickTime, clickDate, clickMin);
}

async function showParallelAtTime(sessions, clickTime, clickDate, clickMin) {
  document.querySelectorAll('.tl-node').forEach(c => c.classList.remove('active'));
  document.querySelectorAll('.gantt-bar').forEach(c => c.classList.remove('active'));
  for (const s of sessions) {
    document.querySelectorAll(`[data-sid="${s.id}"]`).forEach(c => c.classList.add('active'));
  }

  currentSessionId = null;
  const panel = document.getElementById('message-panel');
  document.getElementById('jump-bar').classList.add('hidden');
  document.getElementById('session-header').classList.add('hidden');
  hideMinimap();

  panel.innerHTML = '<div class="flex items-center justify-center h-full gap-2 text-gray-400 text-sm"><span class="spinner"></span> Loading parallel sessions...</div>';

  await Promise.all(sessions.map(s => loadSessionMessages(s.id)));
  const msgCache = getMsgCache();

  let html = `<div class="sticky top-0 z-10 bg-white border-b border-gray-200 px-4 py-2 flex items-center gap-3">
    <span class="text-xs font-bold text-red-500 font-mono">${clickTime}</span>
    <span class="text-xs text-gray-400">${formatDate(clickDate)}</span>
    <span class="text-xs text-gray-500">${sessions.length} parallel sessions</span>
    <button data-action="close-parallel" class="ml-auto text-[10px] text-gray-400 hover:text-gray-600 px-2 py-0.5 rounded hover:bg-gray-100">close</button>
  </div>`;

  const panesCls = sessions.length <= 2 ? ` panes-${sessions.length}` : '';
  html += `<div class="split-view${panesCls}" style="height: calc(100% - 40px);">`;

  for (const s of sessions) {
    const src = SRC[s.source] || SRC_DEFAULT;
    const msgs = msgCache[s.id] || [];
    const repoShort = s.repo_name ? String(s.repo_name).split('/').pop() : '';

    const clickTs = new Date(`${clickDate}T${String(Math.floor(clickMin / 60)).padStart(2, '0')}:${String(Math.floor(clickMin % 60)).padStart(2, '0')}:00Z`).getTime();
    // Find nearest user prompt to clicked time
    let closestUserIdx = -1;
    let closestUserDist = Infinity;
    let closestUserPromptNum = 0;
    let promptCounter = 0;
    msgs.forEach((m, i) => {
      if (m.type === 'user') {
        promptCounter++;
        const mTs = new Date(m.timestamp).getTime();
        const dist = Math.abs(mTs - clickTs);
        if (dist < closestUserDist) { closestUserDist = dist; closestUserIdx = i; closestUserPromptNum = promptCounter; }
      }
    });

    const paneId = 'pane-' + s.id.slice(0, 8);
    const userCount = msgs.filter(m => m.type === 'user').length;
    html += `<div class="split-pane" id="${paneId}">`;
    html += `<div class="split-header">
      <div class="flex items-center gap-2 cursor-pointer hover:bg-blue-50 rounded px-1 -mx-1 transition-colors" data-action="open-session" data-sid="${s.id}" data-source="${s.source}" title="Click to open full session">
        <span class="text-[10px] font-medium px-1.5 py-0.5 rounded ${src.badge}">${src.label}</span>
        <span class="text-[10px] text-gray-400">${msgs.length} msgs</span>
        ${repoShort ? `<span class="text-[10px] text-gray-400">${escapeHtml(repoShort)}</span>` : ''}
        <span class="text-[9px] text-blue-400 ml-auto">open full &rarr;</span>
      </div>
      <div class="flex items-center gap-1.5 mt-1">
        <span class="text-[9px] text-gray-300 font-mono">${s.id.slice(0, 8)}</span>
        ${userCount >= 2 ? `
        <span class="text-gray-200 mx-0.5">|</span>
        <span class="text-[9px] text-gray-400 font-mono split-jump-pos" data-pane="${paneId}">prompt ${closestUserPromptNum || 1}/${userCount}</span>
        <button data-action="split-jump" data-pane="${paneId}" data-dir="-1" class="text-[9px] px-1 py-0.5 rounded hover:bg-gray-100 text-gray-500 font-semibold" title="Previous prompt">&#9650;</button>
        <button data-action="split-jump" data-pane="${paneId}" data-dir="1" class="text-[9px] px-1 py-0.5 rounded hover:bg-gray-100 text-gray-500 font-semibold" title="Next prompt">&#9660;</button>
        ` : ''}
      </div>
    </div>`;
    html += `<div class="split-msgs">`;

    for (let i = 0; i < msgs.length; i++) {
      const m = msgs[i];
      if (m.type === 'progress') continue;
      if (m.type === 'tool_call' && !m.content && (!m.tool || !m.tool.tool_input)) continue;

      const time = m.timestamp.slice(11, 19);
      const st = MSG[m.type] || MSG.system;
      const isNear = i === closestUserIdx ? ' time-highlight' : '';

      html += `<div class="${st.bg} border-l-[3px] ${st.border} rounded-r-lg px-2 py-1.5 mb-1 text-[12px]${isNear}" data-msgtype="${m.type}" ${i === closestUserIdx ? 'id="split-anchor-' + s.id.slice(0, 8) + '"' : ''}>`;
      html += `<div class="flex items-center gap-1.5 flex-wrap">
        <span class="text-[10px] text-gray-400 font-mono">${time}</span>
        <span class="text-[9px] font-bold uppercase ${st.label}">${m.type.replace('_', ' ')}</span>`;
      if (m.tool) html += `<span class="text-[10px] font-semibold text-amber-700">${escapeHtml(m.tool.tool_name)}</span>`;
      if (m.result) {
        const isOk = m.result.status === 'success';
        html += `<span class="text-[9px] font-semibold ${isOk ? 'text-emerald-600' : 'text-red-500'}">${isOk ? 'OK' : 'FAIL'}</span>`;
      }
      html += `</div>`;
      html += renderContent(m.content);
      if (m.tool && m.tool.tool_input) {
        html += makeCollapsible('input', m.tool.tool_input,
          'bg-amber-100/80 text-amber-700 hover:bg-amber-200',
          'bg-amber-50/50 border border-amber-100');
      }
      if (m.result && m.result.output) {
        const isOk = m.result.status === 'success';
        html += makeCollapsible('output', m.result.output,
          isOk ? 'bg-slate-100 text-slate-500 hover:bg-slate-200' : 'bg-red-100 text-red-600 hover:bg-red-200',
          isOk ? 'bg-slate-50 border border-slate-100' : 'bg-red-50 border border-red-100');
      }
      html += `</div>`;
    }

    html += `</div></div>`;
  }
  html += `</div>`;

  panel.innerHTML = html;

  requestAnimationFrame(() => {
    for (const s of sessions) {
      const anchor = document.getElementById('split-anchor-' + s.id.slice(0, 8));
      if (anchor) {
        const pane = anchor.closest('.split-pane');
        if (pane) {
          const headerH = pane.querySelector('.split-header')?.offsetHeight || 0;
          // Align all anchors to the same position: just below the sticky header
          pane.scrollTop = anchor.offsetTop - headerH;
        }
      }
    }
  });
}

export function setupScrollTracking() {
  const panel = document.getElementById('message-panel');
  const progress = document.getElementById('scroll-progress');
  const jumpPos = document.getElementById('jump-pos');
  panel.addEventListener('scroll', () => {
    const pct = panel.scrollHeight <= panel.clientHeight ? 0 : (panel.scrollTop / (panel.scrollHeight - panel.clientHeight)) * 100;
    progress.style.width = pct + '%';

    if (userMsgEls.length) {
      let current = 0;
      for (let i = 0; i < userMsgEls.length; i++) {
        if (userMsgEls[i].offsetTop <= panel.scrollTop + 60) current = i;
      }
      jumpPos.textContent = `prompt ${current + 1}/${userMsgEls.length}`;
    }

    updateViewport();
  });
}
