import type { DeckEvent } from '@/types/workbench';
import type { AgentRunState } from '@/types/workbench';
import { summarizeMarkdown } from '@/lib/markdown';

const DECK_PREFIX = '@vigthoria-deck:';

export function parseDeckLine(line: string): DeckEvent | null {
  const trimmed = line.trim();
  if (!trimmed.startsWith(DECK_PREFIX)) return null;
  try {
    return JSON.parse(trimmed.slice(DECK_PREFIX.length)) as DeckEvent;
  } catch {
    return null;
  }
}

export function stripDeckFromChunk(text: string): string {
  return text
    .split('\n')
    .filter((line) => !line.trim().startsWith(DECK_PREFIX))
    .join('\n');
}

export function applyDeckEvent(state: AgentRunState, event: DeckEvent): AgentRunState {
  switch (event.type) {
    case 'agent_start':
      return {
        ...state,
        status: 'running',
        toolCount: 0,
        iterationCount: 0,
        currentTool: undefined,
        reportMarkdown: '',
        summaryTitle: '',
        summarySections: [],
      };
    case 'tool_start':
      return {
        ...state,
        status: 'running',
        toolCount: event.index ?? state.toolCount + 1,
        currentTool: event.tool,
      };
    case 'tool_end':
      return {
        ...state,
        lastToolSuccess: event.success,
        currentTool: undefined,
      };
    case 'report_ready':
      return {
        ...state,
        reportMarkdown: event.markdown || state.reportMarkdown,
      };
    case 'run_complete':
      return {
        ...state,
        status: event.success === false ? 'failed' : 'complete',
        toolCount: event.tools ?? state.toolCount,
        iterationCount: event.iterations ?? state.iterationCount,
        elapsed: event.elapsed,
      };
    case 'run_summary': {
      const summary = event.title
        ? { title: event.title, sections: event.sections || [] }
        : summarizeMarkdown(state.reportMarkdown);
      return {
        ...state,
        status: event.success === false ? 'failed' : 'complete',
        summaryTitle: summary.title,
        summarySections: summary.sections,
      };
    }
    default:
      return state;
  }
}

// Returns true if `trimmedTail` could still grow into a complete
// "@vigthoria-deck:{...}" marker line once more bytes arrive (either it's a
// prefix of the marker itself, or it already starts with the full marker but
// the JSON payload/closing newline hasn't arrived yet).
function looksLikePartialDeckMarker(trimmedTail: string): boolean {
  if (!trimmedTail) return false;
  return DECK_PREFIX.startsWith(trimmedTail) || trimmedTail.startsWith(DECK_PREFIX);
}

export function extractDeckEventsFromChunk(
  chunk: string,
  carry: string,
  onEvent: (event: DeckEvent) => void,
): { visible: string; carry: string } {
  // A "@vigthoria-deck:{...}" marker line can arrive split across two PTY
  // read chunks (e.g. chunk 1 ends with "...@vigthoria-de" and chunk 2 starts
  // with "ck:{...}\n"). Without carrying the trailing partial line across
  // calls, NEITHER half matches the marker prefix check on its own, so the
  // raw JSON marker leaks into the visible terminal instead of being parsed
  // and hidden. Only hold the tail back when it could genuinely still become
  // a marker line -- otherwise flush immediately so ordinary output isn't
  // delayed.
  const combined = carry + chunk;
  const lines = combined.split('\n');
  const tail = lines.pop() ?? '';

  const visibleLines: string[] = [];
  for (const line of lines) {
    const event = parseDeckLine(line);
    if (event) {
      onEvent(event);
    } else {
      visibleLines.push(line);
    }
  }

  let visible = visibleLines.length > 0 ? `${visibleLines.join('\n')}\n` : '';

  const trimmedTail = tail.trim();
  if (tail && !looksLikePartialDeckMarker(trimmedTail)) {
    visible += tail;
    return { visible, carry: '' };
  }
  return { visible, carry: tail };
}
