import 'dart:async';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:hive_flutter/hive_flutter.dart';

import 'core/enums.dart';
import 'core/hijri_date.dart';
import 'data/local/models.dart';
import 'data/services/hijri_service.dart';
import 'data/services/sync_service.dart';

// ──────────────────────────────────────────────
// SIMULATION (DEVELOPER) MODE
// ──────────────────────────────────────────────
final mockTimeOffsetProvider = StateProvider<Duration>((ref) => Duration.zero);

// ──────────────────────────────────────────────
// CLOCK PROVIDER — fires every second
// ──────────────────────────────────────────────

final clockProvider = StreamProvider<DateTime>((ref) {
  final offset = ref.watch(mockTimeOffsetProvider);
  return Stream.periodic(
    const Duration(seconds: 1),
    (_) => DateTime.now().add(offset),
  );
});

// ──────────────────────────────────────────────
// SETTINGS PROVIDER
// ──────────────────────────────────────────────

final settingsProvider = StateNotifierProvider<SettingsNotifier, AppSettings>(
  (ref) => SettingsNotifier(),
);

class SettingsNotifier extends StateNotifier<AppSettings> {
  SettingsNotifier() : super(_loadSettings());

  static AppSettings _loadSettings() {
    final box = Hive.box<AppSettings>(HiveBoxes.settings);
    return box.get('default')?.copyWith() ?? AppSettings();
  }

  Future<void> updateSettings(AppSettings Function(AppSettings) updater) async {
    final updated = updater(state.copyWith());
    final box = Hive.box<AppSettings>(HiveBoxes.settings);
    await box.put('default', updated);
    state = updated;
  }

  void reload() {
    state = _loadSettings();
  }
}

// ──────────────────────────────────────────────
// TEXT SCALING PROVIDER
// ──────────────────────────────────────────────
final textScaleProvider = Provider<double>((ref) {
  final index = ref.watch(settingsProvider.select((s) => s.textScaleIndex));
  switch (index) {
    case 0: return 0.85; // Small
    case 2: return 1.15; // Large
    case 1:
    default: return 1.0; // Medium
  }
});

// ──────────────────────────────────────────────
// TODAY'S SCHEDULE PROVIDER
// ──────────────────────────────────────────────

final todayScheduleProvider = Provider<DailyPrayerSchedule?>((ref) {
  // Re-read whenever clock date changes (auto-advance at midnight)
  final clock = ref.watch(clockProvider).valueOrNull;
  if (clock == null) return null;
  return SyncService.instance.getTodaySchedule(clock);
});

final scheduleCacheStatusProvider = Provider<ScheduleCacheStatus>((ref) {
  final clock = ref.watch(clockProvider).valueOrNull ?? DateTime.now();
  return SyncService.instance.getCacheStatus(clock);
});

final hijriDateProvider = FutureProvider<String>((ref) async {
  final clock = ref.watch(clockProvider).valueOrNull ?? DateTime.now();
  final hijriOffsetDays =
      ref.watch(settingsProvider.select((s) => s.hijriOffsetDays));
  final dateOnly = DateTime(clock.year, clock.month, clock.day)
      .add(Duration(days: hijriOffsetDays));

  try {
    return await HijriCalendarService.instance.getHijriLabel(dateOnly);
  } catch (_) {
    return HijriDateFormatter.format(dateOnly);
  }
});

// ──────────────────────────────────────────────
// SCREEN STATE MACHINE PROVIDER
// ──────────────────────────────────────────────

/// Computed state that tells the UI which screen to display.
class ScreenStateData {
  final ScreenState state;
  final PrayerName? activePrayer;      // Current or next prayer
  final PrayerName? nextPrayer;
  final Duration? timeUntilNext;       // Countdown to next prayer time
  final Duration? iqomahRemaining;     // Countdown during iqomah state
  final Duration? blankRemaining;      // Countdown during shalat/blank state
  final bool isFriday;
  final DateTime now;

  const ScreenStateData({
    required this.state,
    this.activePrayer,
    this.nextPrayer,
    this.timeUntilNext,
    this.iqomahRemaining,
    this.blankRemaining,
    required this.isFriday,
    required this.now,
  });
}

final screenStateProvider = Provider<ScreenStateData>((ref) {
  final clock = ref.watch(clockProvider).valueOrNull ?? DateTime.now();
  final schedule = ref.watch(todayScheduleProvider);
  final settings = ref.watch(settingsProvider);
  final isFriday = clock.weekday == DateTime.friday;

  if (schedule == null) {
    // No data synced yet — stay in normal mode with no countdown
    return ScreenStateData(
      state: ScreenState.normal,
      isFriday: isFriday,
      now: clock,
    );
  }

  final times = schedule.toDateTimeMap(clock);

  // Build ordered list of fardhu prayer entries
  final fardhList = <MapEntry<PrayerName, DateTime>>[
    MapEntry(PrayerName.subuh, times['subuh']!),
    MapEntry(PrayerName.dzuhur, times['dzuhur']!),
    MapEntry(PrayerName.ashar, times['ashar']!),
    MapEntry(PrayerName.maghrib, times['maghrib']!),
    MapEntry(PrayerName.isya, times['isya']!),
  ];

  int iqomahMinutes(PrayerName p) {
    switch (p) {
      case PrayerName.subuh: return settings.iqomahSubuh;
      case PrayerName.dzuhur: return settings.iqomahDzuhur;
      case PrayerName.ashar: return settings.iqomahAshar;
      case PrayerName.maghrib: return settings.iqomahMaghrib;
      case PrayerName.isya: return settings.iqomahIsya;
      default: return 10;
    }
  }

  int blankMinutes() {
    return isFriday ? settings.blankScreenJumat : settings.blankScreenNormal;
  }

  // Check each prayer window in order (latest first for "current")
  for (int i = fardhList.length - 1; i >= 0; i--) {
    final prayer = fardhList[i];
    final adzanTime = prayer.value;
    final preAdzanTime =
        adzanTime.subtract(Duration(minutes: settings.preAdzanLead));
    final iqomahDuration = Duration(minutes: iqomahMinutes(prayer.key));
    final iqomahEnd = adzanTime.add(iqomahDuration);
    final blankEnd =
        iqomahEnd.add(Duration(minutes: blankMinutes()));

    // STATE: SHALAT (Black Screen)
    if (clock.isAfter(iqomahEnd) && clock.isBefore(blankEnd)) {
      return ScreenStateData(
        state: ScreenState.shalat,
        activePrayer: prayer.key,
        blankRemaining: blankEnd.difference(clock),
        isFriday: isFriday,
        now: clock,
      );
    }

    // STATE: MENUJU IQOMAH (starts after 2-min adzan alert)
    final adzanAlertEnd = adzanTime.add(const Duration(minutes: 2));
    if (clock.isAfter(adzanAlertEnd) && clock.isBefore(iqomahEnd)) {
      return ScreenStateData(
        state: ScreenState.menujuIqomah,
        activePrayer: prayer.key,
        iqomahRemaining: iqomahEnd.difference(clock),
        isFriday: isFriday,
        now: clock,
      );
    }

    // STATE: ADZAN (first 2 minutes after adzan time)
    if (clock.isAfter(adzanTime) && clock.isBefore(adzanAlertEnd)) {
      return ScreenStateData(
        state: ScreenState.adzan,
        activePrayer: prayer.key,
        iqomahRemaining: iqomahEnd.difference(clock),
        isFriday: isFriday,
        now: clock,
      );
    }

    // STATE: MENUJU ADZAN (pre-adzan lock)
    if (clock.isAfter(preAdzanTime) && clock.isBefore(adzanTime)) {
      return ScreenStateData(
        state: ScreenState.menujuAdzan,
        activePrayer: prayer.key,
        nextPrayer: prayer.key,
        timeUntilNext: adzanTime.difference(clock),
        isFriday: isFriday,
        now: clock,
      );
    }
  }

  // STATE: NORMAL — find next upcoming prayer for countdown
  PrayerName? nextPrayer;
  Duration? untilNext;
  for (final prayer in fardhList) {
    if (clock.isBefore(prayer.value)) {
      nextPrayer = prayer.key;
      untilNext = prayer.value.difference(clock);
      break;
    }
  }

  return ScreenStateData(
    state: ScreenState.normal,
    nextPrayer: nextPrayer,
    timeUntilNext: untilNext,
    isFriday: isFriday,
    now: clock,
  );
});

// ──────────────────────────────────────────────
// ROTATION PROVIDER (for Normal state slideshow)
// ──────────────────────────────────────────────

/// Controls the rotation between main screen and slideshow views.
final rotationIndexProvider =
    StateNotifierProvider<RotationNotifier, int>((ref) {
  return RotationNotifier(ref);
});

class RotationNotifier extends StateNotifier<int> {
  final Ref _ref;
  Timer? _timer;
  int _elapsed = 0;

  RotationNotifier(this._ref) : super(0) {
    _startRotation();
  }

  void _startRotation() {
    _timer?.cancel();
    _elapsed = 0;
    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      final screenState = _ref.read(screenStateProvider);
      if (screenState.state != ScreenState.normal) {
        // Don't rotate during special states, reset elapsed
        _elapsed = 0;
        return;
      }

      _elapsed++;
      final settings = _ref.read(settingsProvider);
      final validSlides = settings.slideshowImages.where((i) => i.trim().isNotEmpty).toList();
      final hasContent = validSlides.isNotEmpty;
      if (!hasContent) {
        _elapsed = 0;
        if (state != 0) state = 0; // force main screen state
        return;
      }

      final isMainScreen = state % 2 == 0;
      final duration = isMainScreen
          ? settings.mainScreenDurationSec
          : settings.slideDurationSec;

      if (_elapsed >= duration) {
        _elapsed = 0;
        state = state + 1;
      }
    });
  }

  @override
  void dispose() {
    _timer?.cancel();
    super.dispose();
  }
}

/// Whether we're currently showing the main screen or slideshow.
/// Returns true (main) always if no slideshow images are configured AND
/// Unsplash background is disabled — no point rotating to an empty slide.
final isMainScreenProvider = Provider<bool>((ref) {
  final settings = ref.watch(settingsProvider);
  final validSlides = settings.slideshowImages.where((i) => i.trim().isNotEmpty).toList();
  final hasContent = validSlides.isNotEmpty;
  if (!hasContent) return true; // always stay on main screen

  final index = ref.watch(rotationIndexProvider);
  // Even = main, Odd = slideshow
  return index % 2 == 0;
});
