import '../../marianumconnect/queries/timetable_get_holidays/timetable_get_holidays_response.dart';
import '../../marianumconnect/queries/timetable_get_rooms/timetable_get_rooms_response.dart';
import '../../marianumconnect/queries/timetable_get_schoolyear/timetable_get_schoolyear_response.dart';
import '../../marianumconnect/queries/timetable_get_subjects/timetable_get_subjects_response.dart';
import '../../marianumconnect/queries/timetable_get_timegrid/timetable_get_timegrid_response.dart';
import '../../marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
import '../../mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
import '../demo_persona.dart';

/// Curated timetable fixtures for the demo mode. Everything is derived from
/// [DemoPersona] so subjects, teachers and rooms line up with the rest of the
/// demo app, and every week is generated relative to the requested range so
/// navigating forward/backward always lands on a populated 10b schedule.
class DemoTimetable {
  const DemoTimetable._();

  /// Period → (startHour, startMinute, endHour, endMinute).
  static const Map<int, (int, int, int, int)> _periods = {
    1: (7, 55, 8, 40),
    2: (8, 40, 9, 25),
    3: (9, 45, 10, 30),
    4: (10, 30, 11, 15),
    5: (11, 35, 12, 20),
    6: (12, 20, 13, 5),
    7: (13, 50, 14, 35),
    8: (14, 35, 15, 20),
  };

  /// Weekday (DateTime.monday..friday) → period → subject short code.
  static const Map<int, Map<int, String>> _plan = {
    DateTime.monday: {1: 'M', 2: 'M', 3: 'D', 4: 'E', 5: 'BIO', 6: 'GE', 7: 'SP', 8: 'SP'},
    DateTime.tuesday: {1: 'E', 2: 'E', 3: 'PH', 4: 'M', 5: 'D', 6: 'REL', 7: 'GE'},
    DateTime.wednesday: {1: 'BIO', 2: 'BIO', 3: 'M', 4: 'D', 5: 'E', 6: 'PH'},
    DateTime.thursday: {1: 'D', 2: 'GE', 3: 'M', 4: 'M', 5: 'REL', 6: 'BIO', 7: 'SP', 8: 'SP'},
    DateTime.friday: {1: 'PH', 2: 'PH', 3: 'E', 4: 'M', 5: 'D', 6: 'GE'},
  };

  static const Map<String, String> _roomBySubject = {
    'M': 'A101',
    'D': 'A101',
    'E': 'A102',
    'BIO': 'B201',
    'PH': 'B203',
    'GE': 'A101',
    'SP': 'Turnhalle 1',
    'REL': 'C305',
  };

  static TimetableGetWeekResponse week(DateTime from, DateTime until) {
    final monday = _mondayOf(from);
    final entries = <McTimetableEntry>[];
    var id = 1;

    for (final weekday in _plan.keys) {
      final date = monday.add(Duration(days: weekday - DateTime.monday));
      final day = _plan[weekday]!;
      for (final entry in day.entries) {
        entries.add(_entry(id++, date, weekday, entry.key, entry.value));
      }
    }

    return TimetableGetWeekResponse(from: from, until: until, entries: entries);
  }

  static McTimetableEntry _entry(
    int id,
    DateTime date,
    int weekday,
    int period,
    String subjectShort,
  ) {
    final times = _periods[period]!;
    final teacher = _teacherFor(subjectShort);

    // A couple of recurring irregularities so the demo shows off the cancelled
    // and substitution rendering.
    final cancelled = weekday == DateTime.monday && period == 4;
    final substituted = weekday == DateTime.tuesday && period == 3;

    return McTimetableEntry(
      id: id,
      date: date,
      startTime: DateTime(1970, 1, 1, times.$1, times.$2),
      endTime: DateTime(1970, 1, 1, times.$3, times.$4),
      subjects: [subjectShort],
      teachers: [
        if (substituted)
          McTimetableTeacher(
            shortName: 'MÜL',
            displayName: 'Herr Müller',
            originalShortName: teacher.short,
            originalDisplayName: teacher.name,
          )
        else
          McTimetableTeacher(shortName: teacher.short, displayName: teacher.name),
      ],
      rooms: [_roomBySubject[subjectShort] ?? DemoPersona.rooms.first],
      classNames: [DemoPersona.className],
      lessonType: 'LESSON',
      status: cancelled
          ? 'CANCELLED'
          : substituted
          ? 'IRREGULAR'
          : 'REGULAR',
      substitutionText: substituted ? 'Vertretung für ${teacher.name}' : null,
      lessonText: null,
      infoText: cancelled ? 'Fällt aus' : null,
    );
  }

  static TimetableGetRoomsResponse rooms() => TimetableGetRoomsResponse(
    result: [
      for (var i = 0; i < DemoPersona.rooms.length; i++)
        McRoom(id: i + 1, shortName: DemoPersona.rooms[i], longName: DemoPersona.rooms[i]),
    ],
  );

  static TimetableGetSubjectsResponse subjects() => TimetableGetSubjectsResponse(
    result: [
      for (var i = 0; i < DemoPersona.subjects.length; i++)
        McSubject(
          id: i + 1,
          shortName: DemoPersona.subjects[i].short,
          longName: DemoPersona.subjects[i].name,
        ),
    ],
  );

  static TimetableGetTimegridResponse timegrid() {
    final units = <McTimegridUnit>[];
    for (final day in _weekdays) {
      for (final period in _periods.entries) {
        final t = period.value;
        units.add(
          McTimegridUnit(
            dayOfWeek: day,
            label: '${period.key}',
            startTime: DateTime(1970, 1, 1, t.$1, t.$2),
            endTime: DateTime(1970, 1, 1, t.$3, t.$4),
          ),
        );
      }
    }
    return TimetableGetTimegridResponse(result: units);
  }

  static TimetableGetSchoolyearResponse schoolyear() {
    final startYear = _schoolyearStart(DateTime.now());
    return TimetableGetSchoolyearResponse(
      id: 1,
      name: '$startYear/${startYear + 1}',
      startDate: DateTime(startYear, 8, 1),
      endDate: DateTime(startYear + 1, 7, 31),
    );
  }

  static TimetableGetHolidaysResponse holidays() {
    final startYear = _schoolyearStart(DateTime.now());
    return TimetableGetHolidaysResponse(
      result: [
        McHoliday(
          shortName: 'Herbst',
          longName: 'Herbstferien',
          startDate: DateTime(startYear, 10, 20),
          endDate: DateTime(startYear, 10, 31),
        ),
        McHoliday(
          shortName: 'Weihnachten',
          longName: 'Weihnachtsferien',
          startDate: DateTime(startYear, 12, 22),
          endDate: DateTime(startYear + 1, 1, 6),
        ),
      ],
    );
  }

  static GetCustomTimetableEventResponse customEvents() =>
      GetCustomTimetableEventResponse([]);

  static const List<McDayOfWeek> _weekdays = [
    McDayOfWeek.monday,
    McDayOfWeek.tuesday,
    McDayOfWeek.wednesday,
    McDayOfWeek.thursday,
    McDayOfWeek.friday,
  ];

  static DemoTeacher _teacherFor(String subjectShort) {
    final subjectName = DemoPersona.subjects
        .firstWhere((s) => s.short == subjectShort)
        .name;
    return DemoPersona.teachers.firstWhere((t) => t.subject == subjectName);
  }

  static DateTime _mondayOf(DateTime date) {
    final day = DateTime(date.year, date.month, date.day);
    return day.subtract(Duration(days: day.weekday - DateTime.monday));
  }

  static int _schoolyearStart(DateTime now) =>
      now.month >= 8 ? now.year : now.year - 1;
}
