import 'dart:developer';

import 'package:hydrated_bloc/hydrated_bloc.dart';

import '../../../../../api/demo/data/demo_capabilities.dart';
import '../../../../../api/demo/demo_mode.dart';
import '../../../../../api/marianumconnect/queries/get_capabilities/get_capabilities.dart';
import 'capabilities_state.dart';

/// Holds the current user's mobile capability flags. Hydrated so the last
/// known state is available immediately on cold start (no feature-flicker)
/// and offline. [load] refreshes it from the server after login.
class CapabilitiesCubit extends HydratedCubit<CapabilitiesState> {
  CapabilitiesCubit() : super(const CapabilitiesState());

  bool get canViewForeignTimetables => state.viewForeignTimetables;

  bool get canReceivePushNotifications => state.pushNotifications;

  int? get timetablePastDays => state.timetablePastDays;

  int? get timetableFutureDays => state.timetableFutureDays;

  /// Teacher accounts get the class shown on timetable tiles instead of their
  /// own name (see TimetableAppointmentFactory.showClassInsteadOfTeacher).
  bool get isTeacher => state.userType == 'TEACHER';

  /// Refreshes capabilities from the server. On any failure (endpoint not yet
  /// live, network error, 4xx) the previously hydrated flags are kept but the
  /// state is marked `loaded` — a failed fetch never silently grants a
  /// capability, and an offline launch keeps whatever was cached.
  Future<void> load() async {
    if (DemoMode.active) {
      emit(DemoCapabilities.state());
      return;
    }
    try {
      final response = await GetCapabilities().run();
      emit(
        CapabilitiesState(
          viewForeignTimetables: response.viewForeignTimetables,
          pushNotifications: response.pushNotifications,
          timetablePastDays: response.timetablePastDays,
          timetableFutureDays: response.timetableFutureDays,
          userType: response.userType,
          loaded: true,
        ),
      );
    } catch (e) {
      log('Failed to load capabilities: $e');
      emit(state.copyWith(loaded: true));
    }
  }

  Future<void> reset() async => emit(const CapabilitiesState());

  @override
  CapabilitiesState fromJson(Map<String, dynamic> json) {
    try {
      return CapabilitiesState.fromJson(json);
    } catch (_) {
      return const CapabilitiesState();
    }
  }

  @override
  Map<String, dynamic>? toJson(CapabilitiesState state) => state.toJson();
}
