• Computes reading habit patterns (day of week, time of day) with per-manga baseline from pre-range history.

    Parameters

    Returns {
        byDayOfWeek: { day: string; chapters: number }[];
        byTimeOfDay: { hour: string; chapters: number }[];
        peakDay: null | string;
        peakHour: null | string;
    }

    Habit pattern data (byDayOfWeek, byTimeOfDay, peakDay, peakHour).

    export function computeReadingHabits(
    history: ReadingHistory,
    timeRange: TimeRange,
    ): {
    byDayOfWeek: Array<{ day: string; chapters: number }>;
    byTimeOfDay: Array<{ hour: string; chapters: number }>;
    peakDay: string | null;
    peakHour: string | null;
    } {
    const filtered = filterHistoryByTimeRange(history, timeRange);
    if (!filtered.length) {
    return {
    byDayOfWeek: [],
    byTimeOfDay: [],
    peakDay: null,
    peakHour: null,
    };
    }

    // Establish baseline from pre-range history
    const now = Date.now();
    const ranges = {
    "7d": 7 * 24 * 60 * 60 * 1000,
    "30d": 30 * 24 * 60 * 60 * 1000,
    "90d": 90 * 24 * 60 * 60 * 1000,
    };
    const cutoff = timeRange === "all" ? 0 : now - ranges[timeRange];
    const preRangeBaseline =
    timeRange === "all" ? new Map() : getPreRangeBaseline(history, cutoff);

    const dayNames = [
    "Sunday",
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    ];
    const dayMap = new Map<number, number>();
    const hourMap = new Map<number, number>();

    // Calculate deltas and group by day/hour
    const sorted = [...filtered].sort((a, b) => a.timestamp - b.timestamp);
    const previousChapters = new Map<string | number, number>(preRangeBaseline);

    for (const entry of sorted) {
    const prev = previousChapters.get(entry.mangaId) ?? 0;
    const delta = Math.max(0, entry.chaptersRead - prev);

    if (delta > 0) {
    const date = new Date(entry.timestamp);
    const dayOfWeek = date.getDay();
    const hour = date.getHours();

    dayMap.set(dayOfWeek, (dayMap.get(dayOfWeek) ?? 0) + delta);
    hourMap.set(hour, (hourMap.get(hour) ?? 0) + delta);
    }

    previousChapters.set(entry.mangaId, entry.chaptersRead);
    }

    // Convert to arrays
    const byDayOfWeek = Array.from({ length: 7 }, (_, i) => ({
    day: dayNames[i],
    chapters: dayMap.get(i) ?? 0,
    }));

    const byTimeOfDay = Array.from({ length: 24 }, (_, i) => ({
    hour: `${i.toString().padStart(2, "0")}:00`,
    chapters: hourMap.get(i) ?? 0,
    }));

    // Find peaks
    const maxDay = Math.max(...byDayOfWeek.map((d) => d.chapters));
    const maxHour = Math.max(...byTimeOfDay.map((h) => h.chapters));

    const peakDay = byDayOfWeek.find((d) => d.chapters === maxDay)?.day ?? null;
    const peakHour =
    byTimeOfDay.find((h) => h.chapters === maxHour)?.hour ?? null;

    return {
    byDayOfWeek,
    byTimeOfDay,
    peakDay,
    peakHour,
    };
    }