export type SessionTreeProviderKind = 'file' | 'mysql';

export interface SessionTreeHeader {
  type: 'session';
  version: number;
  id: string;
  timestamp: string;
  cwd: string;
  parentSession?: string;
  leafId?: string | null;
  status?: string;
  title?: string;
  agentId?: number | string;
  userId?: number | string;
  metadata?: Record<string, unknown>;
  updatedAt?: string;
}

export type SessionTreeModelMessageRole = 'system' | 'user' | 'assistant' | 'tool';

export interface SessionTreeMessage {
  id?: string;
  role: SessionTreeModelMessageRole;
  content: unknown;
  name?: string;
  model?: string;
  thinking?: unknown;
  toolCalls?: unknown;
  toolCallId?: string;
  usage?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
  [key: string]: unknown;
}

export interface SessionTreeCustomRuntimeMessage {
  role: 'custom';
  customType: string;
  content: unknown;
  display: boolean;
  details?: Record<string, unknown>;
  timestamp: number;
}

export interface SessionTreeBranchSummaryRuntimeMessage {
  role: 'branchSummary';
  summary: string;
  fromId: string;
  timestamp: number;
}

export interface SessionTreeCompactionSummaryRuntimeMessage {
  role: 'compactionSummary';
  summary: string;
  tokensBefore: number;
  timestamp: number;
}

export type SessionTreeRuntimeMessage =
  | SessionTreeMessage
  | SessionTreeCustomRuntimeMessage
  | SessionTreeBranchSummaryRuntimeMessage
  | SessionTreeCompactionSummaryRuntimeMessage;

export interface SubagentEvidenceSummaryProjection {
  kind: string;
  title: string;
  summary: string;
  files?: string[];
  count?: number;
  previews?: string[];
}

export interface SubagentProcessEventProjection {
  type: string;
  runId?: string;
  timestamp?: string;
  name?: string;
  label?: string;
  toolCallId?: string;
  error?: string;
  result?: unknown;
  args?: unknown;
  text?: string;
  [key: string]: unknown;
}

export interface SubagentToolExecutionProjection {
  key: string;
  toolCallId?: string;
  name?: string;
  label?: string;
  kind: 'tool_call' | 'proxy_tool_call';
  status: 'running' | 'completed';
  args?: unknown;
  result?: unknown;
  error?: string;
  runId?: string;
  startedAt?: string;
  finishedAt?: string;
}

export interface SubagentTaskPanelProjection {
  key: string;
  title: string;
  status?: string;
  sortOrder?: number;
  runId?: string;
  summary?: string;
  error?: string;
  evidenceSummaries: SubagentEvidenceSummaryProjection[];
  processEvents: SubagentProcessEventProjection[];
  toolExecutions: SubagentToolExecutionProjection[];
  toolExecutionsTruncated?: boolean;
  result?: Record<string, unknown>;
}

export interface SubagentProjectionDiagnostics {
  projectionPath?: 'canonical-backend-projection' | 'metadata-only-fallback' | 'legacy-payload-fallback';
  selectedSource: 'subagent_result' | 'tool_message' | 'message_metadata' | 'entry_metadata';
  inputSources: Array<'subagent_result' | 'tool_message' | 'message_metadata' | 'entry_metadata'>;
  ignoredSources: Array<'subagent_result' | 'tool_message' | 'message_metadata' | 'entry_metadata'>;
  rawResultCount: number;
  dedupedResultCount: number;
  projectedTaskCount: number;
  skippedResultCount: number;
  fallbackUsed: boolean;
}

export interface SubagentToolRuntimeRouteProjection {
  name: string;
  workerRoutingHint?: 'local' | 'main-process-proxy' | 'disabled';
  supportedInWorker?: boolean;
  executionMode?: string;
  runtimeRoute?: 'worker-local' | 'main-process-proxy' | 'disabled';
  [key: string]: unknown;
}

export interface SubagentBatchEventProjection {
  sequence?: number;
  timestamp?: string;
  event?: Record<string, unknown>;
  [key: string]: unknown;
}

export interface SessionTreeSubagentBatchMetadata extends Record<string, unknown> {
  latestEvent?: SubagentBatchEventProjection;
  events?: SubagentBatchEventProjection[];
  processEvents?: SubagentProcessEventProjection[];
  finalResults?: Record<string, unknown>[];
  evidenceSummaries?: SubagentEvidenceSummaryProjection[];
  toolRuntimeRoutes?: SubagentToolRuntimeRouteProjection[];
}

export interface SessionTreeSubagentResultMetadata extends Record<string, unknown> {
  finalResults?: Record<string, unknown>[];
  processEvents?: SubagentProcessEventProjection[];
  evidenceSummaries?: SubagentEvidenceSummaryProjection[];
}

export interface SubagentEntryProjection {
  version: 1;
  source: 'backend-projection';
  replay: {
    limits: {
      evidenceSummaries: number;
      processEvents: number;
      taskProcessEvents: number;
      toolExecutions: number;
    };
    truncated: {
      evidenceSummaries: boolean;
      processEvents: boolean;
      taskPanels: boolean;
      toolExecutions: boolean;
    };
  };
  evidenceSummaries: SubagentEvidenceSummaryProjection[];
  processEvents: SubagentProcessEventProjection[];
  taskPanels: SubagentTaskPanelProjection[];
  diagnostics: SubagentProjectionDiagnostics;
}

export interface SessionTreeEntryMetadata extends Record<string, unknown> {
  // Canonical UI projection generated at the backend boundary. Frontend raw payload
  // parsing is only a legacy fallback for historical entries that lack this field.
  subagentProjection?: SubagentEntryProjection;
}

export interface SessionTreeEntryBase {
  id: string;
  parentId: string | null;
  timestamp: string;
  metadata?: SessionTreeEntryMetadata;
}

export interface SessionMessageEntry extends SessionTreeEntryBase {
  type: 'message';
  message: SessionTreeMessage;
}

export interface ThinkingLevelChangeEntry extends SessionTreeEntryBase {
  type: 'thinking_level_change';
  thinkingLevel: string;
}

export interface SessionTreeModelRef {
  provider: string;
  modelId: string;
}

export interface ModelChangeEntry extends SessionTreeEntryBase, SessionTreeModelRef {
  type: 'model_change';
}

export interface CompactionEntry extends SessionTreeEntryBase {
  type: 'compaction';
  summary: string;
  firstKeptEntryId: string;
  tokensBefore: number;
  details?: Record<string, unknown>;
  fromHook?: boolean;
}

export interface BranchSummaryEntry extends SessionTreeEntryBase {
  type: 'branch_summary';
  fromId: string;
  summary: string;
  details?: Record<string, unknown>;
  fromHook?: boolean;
}

export interface CustomEntry extends SessionTreeEntryBase {
  type: 'custom';
  customType: string;
  data?: Record<string, unknown>;
}

export interface CustomMessageEntry extends SessionTreeEntryBase {
  type: 'custom_message';
  customType: string;
  content: unknown;
  details?: Record<string, unknown>;
  display: boolean;
}

export interface LabelEntry extends SessionTreeEntryBase {
  type: 'label';
  targetId: string;
  // `undefined` means clearing the current label on the target entry.
  label: string | undefined;
}

export interface SessionInfoEntry extends SessionTreeEntryBase {
  type: 'session_info';
  name?: string;
}

export interface SubagentBatchEntry extends SessionTreeEntryBase {
  type: 'subagent_batch';
  metadata?: SessionTreeSubagentBatchMetadata;
  content: {
    batchId: string;
    mode: 'single' | 'parallel';
    tasks: Record<string, unknown>[];
    status: 'running' | 'completed';
    parentEntryId?: string | null;
  };
}

export interface SubagentResultEntry extends SessionTreeEntryBase {
  type: 'subagent_result';
  metadata?: SessionTreeSubagentResultMetadata;
  content: {
    batchId: string;
    results: Record<string, unknown>[];
    status: 'running' | 'completed';
    parentEntryId?: string | null;
  };
}

export type SessionTreeEntry =
  | SessionMessageEntry
  | ThinkingLevelChangeEntry
  | ModelChangeEntry
  | CompactionEntry
  | BranchSummaryEntry
  | CustomEntry
  | CustomMessageEntry
  | LabelEntry
  | SessionInfoEntry
  | SubagentBatchEntry
  | SubagentResultEntry;

type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
type UnionKeys<T> = T extends unknown ? keyof T : never;
type StrictUnionHelper<T, AllKeys extends PropertyKey> = T extends unknown
  ? T & Partial<Record<Exclude<AllKeys, keyof T>, never>>
  : never;
type StrictUnion<T> = StrictUnionHelper<T, UnionKeys<T>>;

type EntryPatchBase = {
  metadata?: Record<string, unknown>;
};

export type AppendSessionTreeEntryInput = DistributiveOmit<SessionTreeEntry, 'id' | 'timestamp'> & {
  id?: string;
  timestamp?: string;
};

export type UpdateSessionTreeEntryPatch = StrictUnion<
  | (EntryPatchBase & {
      message?: SessionTreeMessage;
    })
  | (EntryPatchBase & {
      content?: unknown;
      details?: Record<string, unknown>;
      display?: boolean;
    })
  | (EntryPatchBase & {
      data?: Record<string, unknown>;
    })
>;

export type SessionTreeEntryIndex =
  | ReadonlyMap<string, SessionTreeEntry>
  | Readonly<Record<string, SessionTreeEntry>>;

export interface SessionTreeSession {
  sessionId: string;
  provider: SessionTreeProviderKind;
  rootEntryId: string | null;
  leafEntryId: string | null;
  cwd?: string;
  sessionFile?: string;
  parentSessionId?: string;
  agentId?: number | string;
  userId?: number | string;
  title?: string;
  status?: string;
  metadata?: Record<string, unknown>;
  createdAt?: string;
  updatedAt?: string;
}

export interface CreateSessionTreeInput {
  sessionId?: string;
  provider?: SessionTreeProviderKind;
  rootEntryId?: string | null;
  leafEntryId?: string | null;
  cwd?: string;
  sessionFile?: string;
  parentSessionId?: string;
  agentId?: number | string;
  userId?: number | string;
  title?: string;
  status?: string;
  metadata?: Record<string, unknown>;
  createdAt?: string;
  updatedAt?: string;
}

export interface SessionTreeLabelState {
  label: string;
  timestamp: string;
  entryId: string;
}

export interface SessionTreeRuntimeContext {
  messages: SessionTreeRuntimeMessage[];
  thinkingLevel: string;
  model: SessionTreeModelRef | null;
}

export interface SessionTreeSnapshot {
  session: SessionTreeSession;
  entries: SessionTreeEntry[];
  activePath: SessionTreeEntry[];
  childrenByParentId: Record<string, string[]>;
  labelsByEntryId: Record<string, SessionTreeLabelState>;
  runtimeContext: SessionTreeRuntimeContext;
}
