import 'dart:developer';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

import '../state/app/modules/timetable/bloc/timetable_state.dart';
import '../storage/settings.dart';
import 'widget_data_mapper.dart';
import 'widget_sync.dart';

/// Pushes timetable state to the native widget whenever the foreground bloc
/// has fresh data, so the widget doesn't have to wait for the next periodic
/// background fetch.
class WidgetPublisher {
  /// Debug-only "now" offset. Gated by [kDebugMode] so a stray non-zero
  /// value cannot ship in release.
  static const Duration debugTimeShift = Duration.zero;

  static DateTime widgetNow() =>
      kDebugMode ? DateTime.now().add(debugTimeShift) : DateTime.now();

  static Future<void> publishFromBlocState(
    TimetableState state, {
    Settings? settings,
    bool isTeacher = false,
  }) async {
    try {
      final connectDouble =
          settings?.timetableSettings.connectDoubleLessons ?? true;
      // Mirror into widget storage so the background isolate sees the same
      // values the user just toggled — concurrently, they are independent.
      await Future.wait([
        WidgetSync.setConnectDoubleLessons(connectDouble),
        WidgetSync.setThemeMode(_themeName(settings?.appTheme)),
        WidgetSync.setIsTeacher(isTeacher),
      ]);
      final lessons = state.getAllKnownLessons();
      final now = widgetNow();
      final dayData = WidgetDataMapper.buildDayData(
        now: now,
        lessons: lessons,
        subjects: state.subjects,
        rooms: state.rooms,
        holidays: state.schoolHolidays,
        timegrid: state.timegrid,
        customEvents: state.customEvents,
        connectDoubleLessons: connectDouble,
        showClassInsteadOfTeacher: isTeacher,
      );
      final weekData = WidgetDataMapper.buildWeekData(
        now: now,
        lessons: lessons,
        subjects: state.subjects,
        rooms: state.rooms,
        holidays: state.schoolHolidays,
        timegrid: state.timegrid,
        customEvents: state.customEvents,
        connectDoubleLessons: connectDouble,
        showClassInsteadOfTeacher: isTeacher,
      );
      await WidgetSync.writeDayData(dayData);
      await WidgetSync.writeWeekData(weekData);
      await WidgetSync.setLoggedIn(true);
      await WidgetSync.triggerUpdate();
    } on Object catch (e, s) {
      // Catch Object: non-Exception Errors (RangeError, StateError) from the
      // bloc layer must not escape into the stream listener.
      log('WidgetPublisher.publishFromBlocState failed: $e', stackTrace: s);
    }
  }

  static String _themeName(ThemeMode? mode) {
    switch (mode) {
      case ThemeMode.light:
        return 'light';
      case ThemeMode.dark:
        return 'dark';
      case ThemeMode.system:
      case null:
        return 'system';
    }
  }
}
