import 'dart:async';
import 'dart:convert';
import 'dart:developer';

import 'package:home_widget/home_widget.dart';

import 'widget_data.dart';

/// Bridge to the native widget host. All keys/names live here so the Kotlin
/// and Swift sides stay in sync.
class WidgetSync {
  static const String iosAppGroupId =
      'group.eu.mhsl.marianum.mobile.client.widget';

  // Must match the WidgetKit `kind` strings declared in
  // TimetableWidgetExtension.swift — a mismatch makes reloadTimelines a no-op.
  static const String iosDayWidgetKind = 'TimetableDayWidget';
  static const String iosWeekWidgetKind = 'TimetableWeekWidget';
  static const String androidDayProvider = 'TimetableDayWidget';
  static const String androidWeekProvider = 'TimetableWeekWidget';

  // Version suffix lets a schema change invalidate stale snapshots by
  // bumping the key instead of risking a parse crash.
  static const String dayDataKey = 'widget_data_day_v1';
  // v2: 14-day window + per-day `days` holiday info.
  static const String weekDataKey = 'widget_data_week_v2';
  static const String fetchedAtKey = 'widget_data_fetched_at_v1';
  static const String loggedInKey = 'widget_data_logged_in_v1';
  // Mirrored into widget storage so the background isolate can read it
  // without reopening HydratedBloc storage.
  static const String connectDoubleLessonsKey =
      'widget_setting_connect_double_lessons_v1';
  static const String themeModeKey = 'widget_setting_theme_mode_v1';
  // Mirrored from CapabilitiesCubit so the background isolate can render
  // teacher plans (class instead of teacher name) without bloc storage.
  static const String isTeacherKey = 'widget_setting_is_teacher_v1';
  // Mirrored so the background isolate hits the same Marianum-Connect base
  // URL the in-app settings cubit currently has selected.
  static const String marianumConnectBaseUrlKey = 'widget_setting_mc_base_url_v1';

  static bool _initialised = false;

  static Future<void> ensureInitialized() async {
    if (_initialised) return;
    await HomeWidget.setAppGroupId(iosAppGroupId);
    _initialised = true;
  }

  static Future<void> writeDayData(WidgetTimetableData data) =>
      _writeData(dayDataKey, data);

  static Future<void> writeWeekData(WidgetTimetableData data) =>
      _writeData(weekDataKey, data);

  static Future<void> _writeData(String key, WidgetTimetableData data) async {
    await ensureInitialized();
    await HomeWidget.saveWidgetData<String>(key, jsonEncode(data.toJson()));
    await HomeWidget.saveWidgetData<String>(
      fetchedAtKey,
      data.fetchedAt.toIso8601String(),
    );
  }

  static Future<void> setLoggedIn(bool loggedIn) =>
      _setBool(loggedInKey, loggedIn);

  static Future<void> setConnectDoubleLessons(bool value) =>
      _setBool(connectDoubleLessonsKey, value);

  /// Default `true` matches `default_settings.dart` — fresh install behaves
  /// like the in-app calendar.
  static Future<bool> getConnectDoubleLessons() =>
      _getBool(connectDoubleLessonsKey, defaultValue: true);

  static Future<void> setIsTeacher(bool value) => _setBool(isTeacherKey, value);

  static Future<bool> getIsTeacher() =>
      _getBool(isTeacherKey, defaultValue: false);

  static Future<void> _setBool(String key, bool value) async {
    await ensureInitialized();
    await HomeWidget.saveWidgetData<bool>(key, value);
  }

  static Future<bool> _getBool(String key, {required bool defaultValue}) async {
    await ensureInitialized();
    return await HomeWidget.getWidgetData<bool>(key) ?? defaultValue;
  }

  static Future<void> setThemeMode(String mode) async {
    await ensureInitialized();
    await HomeWidget.saveWidgetData<String>(themeModeKey, mode);
  }

  static Future<void> setMarianumConnectBaseUrl(String url) async {
    await ensureInitialized();
    await HomeWidget.saveWidgetData<String>(marianumConnectBaseUrlKey, url);
  }

  static Future<String?> getMarianumConnectBaseUrl() async {
    await ensureInitialized();
    return HomeWidget.getWidgetData<String>(marianumConnectBaseUrlKey);
  }

  static Future<DateTime?> getFetchedAt() async {
    await ensureInitialized();
    final raw = await HomeWidget.getWidgetData<String>(fetchedAtKey);
    return raw == null ? null : DateTime.tryParse(raw);
  }

  static Future<void> clear() async {
    await ensureInitialized();
    await HomeWidget.saveWidgetData<String>(dayDataKey, null);
    await HomeWidget.saveWidgetData<String>(weekDataKey, null);
    await HomeWidget.saveWidgetData<String>(fetchedAtKey, null);
    await HomeWidget.saveWidgetData<bool>(loggedInKey, false);
  }

  static Future<void> triggerUpdate() async {
    await ensureInitialized();
    try {
      await HomeWidget.updateWidget(
        androidName: androidDayProvider,
        iOSName: iosDayWidgetKind,
      );
      await HomeWidget.updateWidget(
        androidName: androidWeekProvider,
        iOSName: iosWeekWidgetKind,
      );
    } on Exception catch (e) {
      log('WidgetSync.triggerUpdate failed: $e');
    }
  }
}
