Computes drill-down data for a specific filter dimension.

Filtered match results.

Type of drill-down:

  • 'genre': Filters by selected match genres
  • 'format': Filters by selected match format
  • 'status': Filters by match status (pending/matched/manual) - NOTE: Different from kenmeiManga.status (reading status)
  • 'date': Filters by match date

Specific value to drill down into.

Reading history for chapter counts.

Drill-down data with detailed breakdown.

export function computeDrillDownData(
matchResults: NormalizedMatchForStats[],
type: "genre" | "format" | "status" | "date",
value: string,
readingHistory: ReadingHistory,
): import("@/types/statistics").DrillDownData {
let filtered: NormalizedMatchForStats[] = [];

switch (type) {
case "genre":
filtered = matchResults.filter((match) => {
const genres = match.selectedMatch?.genres ?? [];
return genres.includes(value);
});
break;
case "format":
filtered = matchResults.filter(
(match) => match.selectedMatch?.format === value,
);
break;
case "status":
filtered = matchResults.filter((match) => match.status === value);
break;
case "date":
// For date drill-down, filter by specific date
filtered = matchResults.filter((match) => {
if (!match.matchDate) return false;
const matchDateStr = getDateKey(match.matchDate.getTime());
return matchDateStr === value;
});
break;
}

// Build detailed data
const data = filtered.map((match) => {
const mangaId = String(match.kenmeiManga.id);
const mangaHistory = readingHistory.entries.filter(
(entry) => String(entry.mangaId) === mangaId,
);

// Sort by timestamp and get the latest entry to ensure correct chapter count
const latestEntry =
mangaHistory.length > 0
? mangaHistory.toSorted((a, b) => a.timestamp - b.timestamp).at(-1)
: null;
const chapters = latestEntry?.chaptersRead ?? 0;

// Compute confidence as selectedMatch.confidence or max across all AniList matches
const selectedConfidence = match.selectedMatch?.confidence;
const allConfidences = (
match.anilistMatches?.map((m) => m.confidence ?? 0) || [0]
).filter(Number.isFinite);
const maxConfidence =
allConfidences.length > 0 ? Math.max(...allConfidences) : 0;
const confidence = selectedConfidence ?? maxConfidence;

return {
title: match.kenmeiManga.title,
chapters,
status: match.status,
confidence,
format: match.selectedMatch?.format,
};
});

// Sort by chapters read (descending) and limit to top 100
data.sort((a, b) => b.chapters - a.chapters);
const limitedData = data.slice(0, 100);

return {
type,
value,
data: limitedData,
};
}