/**
 * SessionSidebar — project-grouped session list with "New Session" entry
 * and project picker mode.
 *
 * Sessions are grouped by project name. Each project appears once as a header,
 * with its sessions listed below. Discovers sessions from both the SSE snapshot
 * AND traffic events so closed sessions still appear.
 *
 * selectedIdx: -2 = "New Session", -1 = all sessions, 0+ = individual sessions
 */

import { Box, Text } from 'ink';
import type { ActiveSession, TimelineEvent } from '../unified-types.js';

interface SessionSidebarProps {
  interactiveSessionId: string | undefined;
  observedSessions: ActiveSession[];
  events: TimelineEvent[];
  selectedIdx: number; // -2 = new session, -1 = all, 0+ = session
  height: number;
  projectName: string;
  mode: 'sessions' | 'project-picker';
  availableProjects: string[];
  projectPickerIdx: number;
}

interface SessionEntry {
  sessionId: string;
  projectName: string;
}

interface ProjectGroup {
  projectName: string;
  sessions: SessionEntry[];
}

export function SessionSidebar({
  interactiveSessionId,
  observedSessions,
  events,
  selectedIdx,
  height,
  projectName,
  mode,
  availableProjects,
  projectPickerIdx,
}: SessionSidebarProps) {
  if (mode === 'project-picker') {
    return (
      <ProjectPicker
        projects={availableProjects}
        selectedIdx={projectPickerIdx}
        height={height}
      />
    );
  }

  const sessions = buildSessionList(interactiveSessionId, observedSessions, events, projectName);
  const groups = groupByProject(sessions);

  // Count events per session
  const counts = new Map<string, number>();
  for (const e of events) {
    counts.set(e.sessionId, (counts.get(e.sessionId) ?? 0) + 1);
  }

  const headerLines = 3; // "Sessions (N)" + "New Session" + "all sessions"
  const footerLines = 5; // keybinding help box
  const bodyHeight = Math.max(1, height - headerLines - footerLines);

  // Build flat render lines for scrolling
  interface RenderLine {
    type: 'project-header' | 'session';
    projectName: string;
    sessionId?: string;
    flatSessionIdx?: number;
  }

  const lines: RenderLine[] = [];
  let flatIdx = 0;
  for (const group of groups) {
    lines.push({ type: 'project-header', projectName: group.projectName });
    for (const s of group.sessions) {
      lines.push({ type: 'session', projectName: group.projectName, sessionId: s.sessionId, flatSessionIdx: flatIdx });
      flatIdx++;
    }
  }

  // Find which render line corresponds to the selected session
  let selectedLineIdx = -1;
  if (selectedIdx >= 0) {
    selectedLineIdx = lines.findIndex((l) => l.flatSessionIdx === selectedIdx);
  }

  // Scroll to keep selected visible
  let scrollStart = 0;
  if (selectedLineIdx >= 0) {
    if (selectedLineIdx >= scrollStart + bodyHeight) {
      scrollStart = selectedLineIdx - bodyHeight + 1;
    }
    if (selectedLineIdx < scrollStart) {
      scrollStart = selectedLineIdx;
    }
  }
  scrollStart = Math.max(0, scrollStart);

  const visibleLines = lines.slice(scrollStart, scrollStart + bodyHeight);
  const hasMore = scrollStart + bodyHeight < lines.length;

  return (
    <Box
      flexDirection="column"
      width={32}
      borderStyle="round"
      borderColor="gray"
      paddingX={1}
      height={height}
    >
      <Text bold color="cyan">
        {' Sessions '}
        <Text dimColor>({sessions.length})</Text>
      </Text>

      {/* "New Session" row */}
      <Text color={selectedIdx === -2 ? 'cyan' : 'green'} bold={selectedIdx === -2}>
        {selectedIdx === -2 ? ' \u25b8 ' : '   '}
        {'+ New Session'}
      </Text>

      {/* "All sessions" row */}
      <Text color={selectedIdx === -1 ? 'cyan' : undefined} bold={selectedIdx === -1}>
        {selectedIdx === -1 ? ' \u25b8 ' : '   '}
        {'all sessions'}
      </Text>

      {/* Grouped session list */}
      {sessions.length === 0 && (
        <Box marginTop={1}>
          <Text dimColor>{'   waiting for connections\u2026'}</Text>
        </Box>
      )}

      {visibleLines.map((line, vi) => {
        if (line.type === 'project-header') {
          return (
            <Text key={`proj-${line.projectName}-${vi}`} bold wrap="truncate">
              {'  '}{line.projectName}
            </Text>
          );
        }

        // Session line
        const isSelected = line.flatSessionIdx === selectedIdx;
        const count = counts.get(line.sessionId!) ?? 0;
        const isInteractive = line.sessionId === interactiveSessionId;

        return (
          <Text key={line.sessionId!} wrap="truncate">
            <Text color={isSelected ? 'cyan' : undefined} bold={isSelected}>
              {isSelected ? '  \u25b8 ' : '    '}
              {line.sessionId!.slice(0, 8)}
            </Text>
            {count > 0 && <Text dimColor>{` \u00b7 ${count} ev`}</Text>}
            {isInteractive && <Text color="green">{' *'}</Text>}
          </Text>
        );
      })}

      {hasMore && (
        <Text dimColor>{'   \u2026 more'}</Text>
      )}

      {/* Spacer */}
      <Box flexGrow={1} />

      {/* Help */}
      <Box borderStyle="single" borderTop borderColor="gray" paddingTop={0}>
        <Text dimColor>
          {'[\u2191\u2193] session [a] all\n[\u23ce] select  [Esc] close\n[x] clear   [q] quit'}
        </Text>
      </Box>
    </Box>
  );
}

/** Project picker sub-view */
function ProjectPicker({
  projects,
  selectedIdx,
  height,
}: {
  projects: string[];
  selectedIdx: number;
  height: number;
}) {
  const headerLines = 2;
  const footerLines = 4;
  const bodyHeight = Math.max(1, height - headerLines - footerLines);

  let scrollStart = 0;
  if (selectedIdx >= scrollStart + bodyHeight) {
    scrollStart = selectedIdx - bodyHeight + 1;
  }
  if (selectedIdx < scrollStart) {
    scrollStart = selectedIdx;
  }
  scrollStart = Math.max(0, scrollStart);

  const visibleProjects = projects.slice(scrollStart, scrollStart + bodyHeight);
  const hasMore = scrollStart + bodyHeight < projects.length;

  return (
    <Box
      flexDirection="column"
      width={32}
      borderStyle="round"
      borderColor="cyan"
      paddingX={1}
      height={height}
    >
      <Text bold color="cyan">
        {' Select Project '}
      </Text>

      {projects.length === 0 ? (
        <Box marginTop={1}>
          <Text dimColor>{'   no projects found'}</Text>
        </Box>
      ) : (
        visibleProjects.map((name, vi) => {
          const realIdx = scrollStart + vi;
          const isSelected = realIdx === selectedIdx;
          return (
            <Text key={name} wrap="truncate">
              <Text color={isSelected ? 'cyan' : undefined} bold={isSelected}>
                {isSelected ? ' \u25b8 ' : '   '}
                {name}
              </Text>
            </Text>
          );
        })
      )}

      {hasMore && (
        <Text dimColor>{'   \u2026 more'}</Text>
      )}

      {/* Spacer */}
      <Box flexGrow={1} />

      {/* Help */}
      <Box borderStyle="single" borderTop borderColor="gray" paddingTop={0}>
        <Text dimColor>
          {'[\u2191\u2193] pick  [\u23ce] select\n[Esc] back'}
        </Text>
      </Box>
    </Box>
  );
}

/** Total session count across all groups */
export function getSessionCount(
  interactiveSessionId: string | undefined,
  observedSessions: ActiveSession[],
  events: TimelineEvent[],
  projectName: string,
): number {
  return buildSessionList(interactiveSessionId, observedSessions, events, projectName).length;
}

function buildSessionList(
  interactiveSessionId: string | undefined,
  observedSessions: ActiveSession[],
  events: TimelineEvent[],
  projectName: string,
): SessionEntry[] {
  const result: SessionEntry[] = [];
  const seen = new Set<string>();

  // Interactive session first
  if (interactiveSessionId) {
    result.push({ sessionId: interactiveSessionId, projectName });
    seen.add(interactiveSessionId);
  }

  // Then observed sessions from SSE snapshot
  for (const s of observedSessions) {
    if (!seen.has(s.sessionId)) {
      result.push({ sessionId: s.sessionId, projectName: s.projectName });
      seen.add(s.sessionId);
    }
  }

  // Also discover sessions from traffic events (covers sessions that
  // were already closed before the SSE connected)
  for (const e of events) {
    if (!seen.has(e.sessionId)) {
      result.push({ sessionId: e.sessionId, projectName: e.projectName });
      seen.add(e.sessionId);
    }
  }

  return result;
}

function groupByProject(sessions: SessionEntry[]): ProjectGroup[] {
  const map = new Map<string, SessionEntry[]>();
  const order: string[] = [];

  for (const s of sessions) {
    let group = map.get(s.projectName);
    if (!group) {
      group = [];
      map.set(s.projectName, group);
      order.push(s.projectName);
    }
    group.push(s);
  }

  return order.map((name) => ({ projectName: name, sessions: map.get(name)! }));
}
