import Foundation

/// Pure anchor/slicing logic mirroring `lib/widget_data/widget_data_mapper.dart`
/// (resolveDayAnchor / resolveWeekAnchor). Keep both sides in sync — the Dart
/// unit tests in test/widget_data/widget_data_mapper_test.dart double as the
/// review checklist here:
///   resolveDayAnchor(Wed 2026-05-06 10:00) == 2026-05-06   (before cutoff)
///   resolveDayAnchor(Wed 2026-05-06 19:00) == 2026-05-07   (after cutoff)
///   resolveDayAnchor(Fri 2026-05-08 18:00) == 2026-05-11   (Fri → Mon)
///   resolveDayAnchor(Sat 2026-05-09 10:00) == 2026-05-11
///   resolveDayAnchor(Sun 2026-05-10 22:00) == 2026-05-11
///   resolveWeekAnchor(Tue 2026-05-05 10:00) == 2026-05-04
///   resolveWeekAnchor(Sun 2026-05-10 10:00) == 2026-05-11
enum TimetableAnchor {
    /// After 17:00 the user's question shifts from "what's left today" to
    /// "what's tomorrow", so the day widget rolls forward.
    static let dayCutoffHour = 17

    static func resolveDayAnchor(_ now: Date, calendar: Calendar = .current) -> Date {
        var candidate = calendar.startOfDay(for: now)
        let shiftToTomorrow =
            calendar.component(.hour, from: now) >= dayCutoffHour || isWeekend(candidate, calendar: calendar)
        if shiftToTomorrow {
            candidate = nextDay(candidate, calendar: calendar)
        }
        while isWeekend(candidate, calendar: calendar) {
            candidate = nextDay(candidate, calendar: calendar)
        }
        return candidate
    }

    static func resolveWeekAnchor(_ now: Date, calendar: Calendar = .current) -> Date {
        let anchor = resolveDayAnchor(now, calendar: calendar)
        // Swift weekday: 1 = Sunday … 7 = Saturday → distance back to Monday.
        let daysFromMonday = (calendar.component(.weekday, from: anchor) + 5) % 7
        let monday = calendar.date(byAdding: .day, value: -daysFromMonday, to: anchor) ?? anchor
        return calendar.startOfDay(for: monday)
    }

    /// Future instants at which the rendered anchor can change: each day's
    /// midnight (rollover) and 17:00 (cutoff), within the horizon. Sorted,
    /// strictly after `now`.
    static func boundaryDates(
        from now: Date,
        horizonDays: Int = 3,
        calendar: Calendar = .current
    ) -> [Date] {
        var result: [Date] = []
        let today = calendar.startOfDay(for: now)
        for offset in 0...horizonDays {
            guard let day = calendar.date(byAdding: .day, value: offset, to: today) else { continue }
            let midnight = calendar.startOfDay(for: day)
            if midnight > now { result.append(midnight) }
            if let cutoff = calendar.date(
                bySettingHour: dayCutoffHour, minute: 0, second: 0, of: day
            ), cutoff > now {
                result.append(cutoff)
            }
        }
        return result.sorted()
    }

    /// Derives a day payload from the 14-day week payload — same shape the
    /// Dart buildDayData produces, since the week payload runs through the
    /// identical per-day merge/collision pipeline.
    static func slice(
        week: WidgetTimetableData,
        forDay anchor: Date,
        calendar: Calendar = .current
    ) -> WidgetTimetableData {
        let lessons = week.lessons.filter { calendar.isDate($0.start, inSameDayAs: anchor) }
        // Every v2 week payload carries `days` — the key bump guarantees it.
        let dayInfo = week.days?.first { calendar.isDate($0.date, inSameDayAs: anchor) }
        return WidgetTimetableData(
            fetchedAt: week.fetchedAt,
            anchorDate: anchor,
            lessons: lessons,
            periods: week.periods,
            isHoliday: dayInfo?.isHoliday ?? false,
            holidayName: dayInfo?.holidayName,
            days: nil
        )
    }

    /// Week payload re-anchored to the week containing/following `anchorDate`.
    /// The week view filters its five columns off `anchorDate`, so this alone
    /// performs the Friday-evening/weekend jump to next week.
    static func retarget(week: WidgetTimetableData, anchorDate: Date) -> WidgetTimetableData {
        WidgetTimetableData(
            fetchedAt: week.fetchedAt,
            anchorDate: anchorDate,
            lessons: week.lessons,
            periods: week.periods,
            isHoliday: week.isHoliday,
            holidayName: week.holidayName,
            days: week.days
        )
    }

    private static func isWeekend(_ date: Date, calendar: Calendar) -> Bool {
        let weekday = calendar.component(.weekday, from: date)
        return weekday == 1 || weekday == 7
    }

    private static func nextDay(_ date: Date, calendar: Calendar) -> Date {
        let next = calendar.date(byAdding: .day, value: 1, to: date) ?? date
        return calendar.startOfDay(for: next)
    }
}
