export interface SessionSummary {
  sessionId: number;
  canonicalTitle: string | null;
  videoId: number | null;
  animeId: number | null;
  animeTitle: string | null;
  startedAtMs: number;
  endedAtMs: number | null;
  totalWatchedMs: number;
  activeWatchedMs: number;
  linesSeen: number;
  tokensSeen: number;
  cardsMined: number;
  lookupCount: number;
  lookupHits: number;
  yomitanLookupCount: number;
  knownWordsSeen: number;
  knownWordRate: number;
}

export interface DailyRollup {
  rollupDayOrMonth: number;
  videoId: number | null;
  totalSessions: number;
  totalActiveMin: number;
  totalLinesSeen: number;
  totalTokensSeen: number;
  totalCards: number;
  cardsPerHour: number | null;
  tokensPerMin: number | null;
  lookupHitRate: number | null;
}

export type MonthlyRollup = DailyRollup;

export interface SessionTimelinePoint {
  sampleMs: number;
  totalWatchedMs: number;
  activeWatchedMs: number;
  linesSeen: number;
  tokensSeen: number;
  cardsMined: number;
}

export interface SessionEvent {
  eventType: number;
  tsMs: number;
  payload: string | null;
}

export interface AnkiNotePreview {
  word: string;
  sentence: string;
  translation: string;
}

export interface StatsAnkiNoteInfo {
  noteId: number;
  fields: Record<string, { value: string }>;
  preview?: AnkiNotePreview;
}

export interface VocabularyEntry {
  wordId: number;
  headword: string;
  word: string;
  reading: string;
  partOfSpeech: string | null;
  pos1: string | null;
  pos2: string | null;
  pos3: string | null;
  frequency: number;
  frequencyRank: number | null;
  animeCount: number;
  firstSeen: number;
  lastSeen: number;
}

export interface StatsExcludedWord {
  headword: string;
  word: string;
  reading: string;
}

/** One animation burst the duplicate-line cleanup found in the stats database. */
export interface StatsDuplicateLineSample {
  videoId: number;
  videoTitle: string | null;
  text: string;
  /** Events recorded for this run, including the one that is kept. */
  frames: number;
  removedLines: number;
  startMs: number;
  endMs: number;
}

export interface StatsDuplicateLineCleanupResult {
  dryRun: boolean;
  lookbackDays: number | null;
  scannedLines: number;
  burstGroups: number;
  removedLines: number;
  removedWordOccurrences: number;
  removedKanjiOccurrences: number;
  samples: StatsDuplicateLineSample[];
}

export interface StatsCoverImage {
  contentType: string;
  dataUrl: string;
}

export interface StatsCoverImagesData {
  anime: Record<number, StatsCoverImage | null>;
  media: Record<number, StatsCoverImage | null>;
}

export interface KanjiEntry {
  kanjiId: number;
  kanji: string;
  frequency: number;
  firstSeen: number;
  lastSeen: number;
}

export interface VocabularyOccurrenceEntry {
  animeId: number | null;
  animeTitle: string | null;
  videoId: number;
  videoTitle: string;
  sourcePath: string | null;
  secondaryText: string | null;
  sessionId: number;
  lineIndex: number;
  segmentStartMs: number | null;
  segmentEndMs: number | null;
  text: string;
  occurrenceCount: number;
}

export interface SentenceSearchResult {
  animeId: number | null;
  animeTitle: string | null;
  videoId: number;
  videoTitle: string;
  sourcePath: string | null;
  secondaryText: string | null;
  sessionId: number;
  lineIndex: number;
  segmentStartMs: number | null;
  segmentEndMs: number | null;
  text: string;
}

export interface OverviewData {
  sessions: SessionSummary[];
  rollups: DailyRollup[];
  hints: {
    totalSessions: number;
    activeSessions: number;
    episodesToday: number;
    activeAnimeCount: number;
    totalEpisodesWatched: number;
    totalAnimeCompleted: number;
    totalActiveMin: number;
    activeDays: number;
    totalCards?: number;
    totalTokensSeen: number;
    totalLookupCount: number;
    totalLookupHits: number;
    totalYomitanLookupCount: number;
    newWordsToday: number;
    newWordsThisWeek: number;
  };
}

export interface MediaLibraryItem {
  videoId: number;
  canonicalTitle: string;
  totalSessions: number;
  totalActiveMs: number;
  totalCards: number;
  totalTokensSeen: number;
  lastWatchedMs: number;
  hasCoverArt: number;
  youtubeVideoId?: string | null;
  videoUrl?: string | null;
  videoTitle?: string | null;
  videoThumbnailUrl?: string | null;
  channelId?: string | null;
  channelName?: string | null;
  channelUrl?: string | null;
  channelThumbnailUrl?: string | null;
  uploaderId?: string | null;
  uploaderUrl?: string | null;
  description?: string | null;
}

export interface MediaDetailData {
  detail: {
    videoId: number;
    canonicalTitle: string;
    animeId: number | null;
    totalSessions: number;
    totalActiveMs: number;
    totalCards: number;
    totalTokensSeen: number;
    totalLinesSeen: number;
    totalLookupCount: number;
    totalLookupHits: number;
    totalYomitanLookupCount: number;
    youtubeVideoId?: string | null;
    videoUrl?: string | null;
    videoTitle?: string | null;
    videoThumbnailUrl?: string | null;
    channelId?: string | null;
    channelName?: string | null;
    channelUrl?: string | null;
    channelThumbnailUrl?: string | null;
    uploaderId?: string | null;
    uploaderUrl?: string | null;
    description?: string | null;
  } | null;
  sessions: SessionSummary[];
  rollups: DailyRollup[];
}

export const EventType = {
  SUBTITLE_LINE: 1,
  MEDIA_BUFFER: 2,
  LOOKUP: 3,
  CARD_MINED: 4,
  SEEK_FORWARD: 5,
  SEEK_BACKWARD: 6,
  PAUSE_START: 7,
  PAUSE_END: 8,
  YOMITAN_LOOKUP: 9,
} as const;

export type EventType = (typeof EventType)[keyof typeof EventType];

export interface AnimeLibraryItem {
  animeId: number;
  canonicalTitle: string;
  anilistId: number | null;
  totalSessions: number;
  totalActiveMs: number;
  totalCards: number;
  totalTokensSeen: number;
  episodeCount: number;
  episodesTotal: number | null;
  lastWatchedMs: number;
}

export interface AnilistEntry {
  anilistId: number;
  titleRomaji: string | null;
  titleEnglish: string | null;
  season: number | null;
}

export interface AnimeDetailData {
  detail: {
    animeId: number;
    canonicalTitle: string;
    anilistId: number | null;
    titleRomaji: string | null;
    titleEnglish: string | null;
    titleNative: string | null;
    description: string | null;
    totalSessions: number;
    totalActiveMs: number;
    totalCards: number;
    totalTokensSeen: number;
    totalLinesSeen: number;
    totalLookupCount: number;
    totalLookupHits: number;
    totalYomitanLookupCount: number;
    episodeCount: number;
    lastWatchedMs: number;
  };
  episodes: AnimeEpisode[];
  anilistEntries: AnilistEntry[];
}

export interface AnimeEpisode {
  videoId: number;
  episode: number | null;
  season: number | null;
  durationMs: number;
  endedMediaMs: number | null;
  watched: number;
  canonicalTitle: string;
  totalSessions: number;
  totalActiveMs: number;
  totalCards: number;
  totalTokensSeen: number;
  totalYomitanLookupCount: number;
  lastWatchedMs: number;
}

export interface AnimeWord {
  wordId: number;
  headword: string;
  word: string;
  reading: string;
  partOfSpeech: string | null;
  frequency: number;
}

export interface StreakCalendarDay {
  epochDay: number;
  totalActiveMin: number;
}

export interface EpisodesPerDay {
  epochDay: number;
  episodeCount: number;
}

export interface NewAnimePerDay {
  epochDay: number;
  newAnimeCount: number;
}

export interface WatchTimePerAnime {
  epochDay: number;
  animeId: number;
  animeTitle: string;
  totalActiveMin: number;
}

export interface TrendChartPoint {
  label: string;
  value: number;
}

export interface TrendPerAnimePoint {
  epochDay: number;
  animeTitle: string;
  value: number;
}

export interface LibrarySummaryRow {
  title: string;
  watchTimeMin: number;
  videos: number;
  sessions: number;
  cards: number;
  words: number;
  lookups: number;
  lookupsPerHundred: number | null;
  firstWatched: number;
  lastWatched: number;
}

export interface TrendsDashboardData {
  activity: {
    watchTime: TrendChartPoint[];
    cards: TrendChartPoint[];
    words: TrendChartPoint[];
    sessions: TrendChartPoint[];
  };
  progress: {
    watchTime: TrendChartPoint[];
    sessions: TrendChartPoint[];
    words: TrendChartPoint[];
    newWords: TrendChartPoint[];
    cards: TrendChartPoint[];
    episodes: TrendChartPoint[];
    lookups: TrendChartPoint[];
  };
  ratios: {
    lookupsPerHundred: TrendChartPoint[];
    cardsPerHour: TrendChartPoint[];
    readingSpeed: TrendChartPoint[];
  };
  librarySummary: LibrarySummaryRow[];
  animeCumulative: {
    watchTime: TrendPerAnimePoint[];
    episodes: TrendPerAnimePoint[];
    cards: TrendPerAnimePoint[];
    words: TrendPerAnimePoint[];
  };
  patterns: {
    watchTimeByDayOfWeek: TrendChartPoint[];
    watchTimeByHour: TrendChartPoint[];
  };
}

export interface WordDetailData {
  detail: {
    wordId: number;
    headword: string;
    word: string;
    reading: string;
    partOfSpeech: string | null;
    pos1: string | null;
    pos2: string | null;
    pos3: string | null;
    frequency: number;
    firstSeen: number;
    lastSeen: number;
  };
  animeAppearances: Array<{
    animeId: number;
    animeTitle: string;
    occurrenceCount: number;
  }>;
  similarWords: Array<{
    wordId: number;
    headword: string;
    word: string;
    reading: string;
    frequency: number;
  }>;
}

export interface EpisodeCardEvent {
  eventId: number;
  sessionId: number;
  tsMs: number;
  cardsDelta: number;
  noteIds: number[];
}

export interface EpisodeDetailData {
  sessions: SessionSummary[];
  words: AnimeWord[];
  cardEvents: EpisodeCardEvent[];
}

export interface KanjiDetailData {
  detail: {
    kanjiId: number;
    kanji: string;
    frequency: number;
    firstSeen: number;
    lastSeen: number;
  };
  animeAppearances: Array<{
    animeId: number;
    animeTitle: string;
    occurrenceCount: number;
  }>;
  words: Array<{
    wordId: number;
    headword: string;
    word: string;
    reading: string;
    frequency: number;
  }>;
}
