import {
  nonEmptyTurnTranscriptSummaries,
  type ThreadTranscriptEntry,
  type TurnTranscriptSummary,
  turnTranscriptSummaryFromTranscriptEntries,
} from "../../domain/threads/transcript";
import type { ThreadItem as GeneratedThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn as GeneratedTurn } from "../../generated/app-server/v2/Turn";

export type TurnItem = GeneratedThreadItem;
export type TurnRecord = GeneratedTurn;

type AppServerUserInput = Extract<TurnItem, { type: "userMessage" }>["content"][number];
type AppServerTextUserInput = Extract<AppServerUserInput, { type: "text" }>;

function transcriptEntriesFromTurnRecord(turn: TurnRecord): ThreadTranscriptEntry[] {
  return turn.items.flatMap((item) => transcriptEntriesFromTurnItem(item, turn));
}

export function transcriptEntriesFromTurnRecords(turns: readonly TurnRecord[]): ThreadTranscriptEntry[] {
  return turns.flatMap(transcriptEntriesFromTurnRecord);
}

function turnTranscriptSummaryFromTurnRecord(turn: TurnRecord): TurnTranscriptSummary {
  return turnTranscriptSummaryFromTranscriptEntries(transcriptEntriesFromTurnRecord(turn));
}

export function turnTranscriptAssistantTextFromTurnRecord(turn: TurnRecord): string | null {
  return turnTranscriptSummaryFromTurnRecord(turn).assistantText;
}

export function completedTurnTranscriptSummaryFromTurnRecord(turn: TurnRecord): TurnTranscriptSummary | null {
  if (turn.status !== "completed") return null;
  const summary = turnTranscriptSummaryFromTurnRecord(turn);
  return summary.userText && summary.assistantText ? summary : null;
}

export function completedTurnTranscriptSummariesFromTurnRecords(turns: readonly TurnRecord[]): TurnTranscriptSummary[] {
  return turns.flatMap((turn) => {
    const summary = completedTurnTranscriptSummaryFromTurnRecord(turn);
    return summary ? [summary] : [];
  });
}

export function chronologicalTurnTranscriptSummariesFromTurnRecords(turns: readonly TurnRecord[]): TurnTranscriptSummary[] {
  return nonEmptyTurnTranscriptSummaries(
    [...turns].sort((a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0)).map(turnTranscriptSummaryFromTurnRecord),
  );
}

export function turnUserItemText(item: Extract<TurnItem, { type: "userMessage" }>): string {
  return userInputText(item.content);
}

export function lastAgentMessageTextFromTurnRecord(turn: TurnRecord): string | null {
  for (let index = turn.items.length - 1; index >= 0; index -= 1) {
    const item = turn.items[index];
    if (item === undefined) continue;
    if (item.type !== "agentMessage") continue;
    const text = item.text.trim();
    if (text) return text;
  }
  return null;
}

function transcriptEntriesFromTurnItem(item: TurnItem, turn: TurnRecord): ThreadTranscriptEntry[] {
  if (item.type === "userMessage") {
    const text = turnUserItemText(item).trim();
    return text ? [{ kind: "user", text, timestamp: turn.startedAt }] : [];
  }
  if (item.type === "agentMessage") {
    const text = item.text.trim();
    return text ? [{ kind: "assistant", text, timestamp: turn.completedAt ?? turn.startedAt }] : [];
  }
  if (item.type === "plan") {
    const text = item.text.trim();
    return text ? [{ kind: "plan", text, timestamp: turn.completedAt ?? turn.startedAt }] : [];
  }
  return [];
}

function userInputText(content: readonly AppServerUserInput[]): string {
  const textItems = content.filter(isTextUserInput);
  const hasText = textItems.some((item) => item.text.length > 0);
  const textIncludes = (value: string) => value.length > 0 && textItems.some((item) => item.text.includes(value));
  return content
    .map((item) => {
      if (item.type === "text") return item.text;
      if (item.type === "localImage") return hasText && textIncludes(item.path) ? "" : `[local image] ${item.path}`;
      if (item.type === "image") return hasText && textIncludes(item.url) ? "" : `[image] ${item.url}`;
      if (item.type === "mention") return hasText ? "" : `[@${item.name}] ${item.path}`;
      return hasText ? "" : `[$${item.name}] ${item.path}`;
    })
    .filter(Boolean)
    .join("\n");
}

function isTextUserInput(item: AppServerUserInput): item is AppServerTextUserInput {
  return item.type === "text";
}
