import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:intl/intl.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/notification_bell_button.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/services/prayer_service.dart'; import '../../../data/services/myquran_sholat_service.dart'; import '../../dashboard/data/prayer_times_provider.dart'; class ImsakiyahScreen extends ConsumerStatefulWidget { const ImsakiyahScreen({super.key}); @override ConsumerState createState() => _ImsakiyahScreenState(); } class _ImsakiyahScreenState extends ConsumerState { int _selectedMonthIndex = 0; late List<_MonthOption> _months; late AppSettings _settings; @override void initState() { super.initState(); final box = Hive.box(HiveBoxes.settings); _settings = box.get('default') ?? AppSettings(); _months = _generateMonths(); // Find current month final now = DateTime.now(); for (int i = 0; i < _months.length; i++) { if (_months[i].month == now.month && _months[i].year == now.year) { _selectedMonthIndex = i; break; } } } List<_MonthOption> _generateMonths() { final now = DateTime.now(); final list = <_MonthOption>[]; for (int offset = -2; offset <= 3; offset++) { final date = DateTime(now.year, now.month + offset, 1); list.add(_MonthOption( label: DateFormat('MMMM yyyy').format(date), month: date.month, year: date.year, )); } return list; } List<_DayRow> _createRows(Map>? apiData) { final selected = _months[_selectedMonthIndex]; final daysInMonth = DateTime(selected.year, selected.month + 1, 0).day; final rows = <_DayRow>[]; for (int d = 1; d <= daysInMonth; d++) { final date = DateTime(selected.year, selected.month, d); final dateStr = DateFormat('yyyy-MM-dd').format(date); if (apiData != null && apiData.containsKey(dateStr)) { final times = apiData[dateStr]!; rows.add(_DayRow( date: date, fajr: times['subuh'] ?? '-', sunrise: times['terbit'] ?? '-', dhuhr: times['dzuhur'] ?? '-', asr: times['ashar'] ?? '-', maghrib: times['maghrib'] ?? '-', isha: times['isya'] ?? '-', )); } else { final times = PrayerService.instance.getPrayerTimes(-6.2088, 106.8456, date); rows.add(_DayRow( date: date, fajr: DateFormat('HH:mm').format(times.fajr), sunrise: DateFormat('HH:mm').format(times.sunrise), dhuhr: DateFormat('HH:mm').format(times.dhuhr), asr: DateFormat('HH:mm').format(times.asr), maghrib: DateFormat('HH:mm').format(times.maghrib), isha: DateFormat('HH:mm').format(times.isha), )); } } return rows; } void _showLocationDialog(BuildContext context) { final searchCtrl = TextEditingController(); bool isSearching = false; List> results = []; Timer? debounce; showDialog( context: context, builder: (ctx) => StatefulBuilder( builder: (ctx, setDialogState) => AlertDialog( insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24), title: const Text('Cari Kota/Kabupaten'), content: SizedBox( width: MediaQuery.of(context).size.width * 0.85, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: searchCtrl, autofocus: true, decoration: InputDecoration( hintText: 'Cth: Jakarta', border: const OutlineInputBorder(), suffixIcon: IconButton( icon: const Icon(LucideIcons.search), onPressed: () async { if (searchCtrl.text.trim().isEmpty) return; setDialogState(() => isSearching = true); final res = await MyQuranSholatService.instance .searchCity(searchCtrl.text.trim()); if (mounted) { setDialogState(() { results = res; isSearching = false; }); } }, ), ), onChanged: (val) { if (val.trim().length < 3) return; if (debounce?.isActive ?? false) debounce!.cancel(); debounce = Timer(const Duration(milliseconds: 500), () async { if (!mounted) return; setDialogState(() => isSearching = true); try { final res = await MyQuranSholatService.instance .searchCity(val.trim()); if (mounted) { setDialogState(() { results = res; }); } } catch (e) { debugPrint('Error searching city: $e'); } finally { if (mounted) { setDialogState(() { isSearching = false; }); } } }); }, onSubmitted: (val) async { if (val.trim().isEmpty) return; if (debounce?.isActive ?? false) debounce!.cancel(); setDialogState(() => isSearching = true); final res = await MyQuranSholatService.instance .searchCity(val.trim()); if (mounted) { setDialogState(() { results = res; isSearching = false; }); } }, ), const SizedBox(height: 16), if (isSearching) const Center(child: CircularProgressIndicator()) else if (results.isEmpty) const Text('Tidak ada hasil', style: TextStyle(color: Colors.grey)) else SizedBox( height: 200, width: double.maxFinite, child: ListView.builder( shrinkWrap: true, itemCount: results.length, itemBuilder: (context, i) { final city = results[i]; return ListTile( title: Text(city['lokasi'] ?? ''), onTap: () { final id = city['id']; final name = city['lokasi']; if (id != null && name != null) { _settings.lastCityName = '$name|$id'; _settings.save(); // Update providers to refresh data ref.invalidate(selectedCityIdProvider); ref.invalidate(cityNameProvider); Navigator.pop(ctx); } }, ); }, ), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx), child: const Text('Batal'), ), ], ), ), ); } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; final today = DateTime.now(); const tableBottomSpacing = 28.0; final selectedMonth = _months[_selectedMonthIndex]; final monthArg = '${selectedMonth.year}-${selectedMonth.month.toString().padLeft(2, '0')}'; final cityNameAsync = ref.watch(cityNameProvider); final monthlyDataAsync = ref.watch(monthlyScheduleProvider(monthArg)); return Scaffold( appBar: AppBar( title: const Text('Kalender Sholat'), centerTitle: false, actions: [ const NotificationBellButton(), IconButton( onPressed: () => context.push('/settings'), icon: const Icon(LucideIcons.settings), ), const SizedBox(width: 8), ], ), body: Column( children: [ // ── Month Selector ── SizedBox( height: 48, child: ListView.separated( scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), itemCount: _months.length, separatorBuilder: (_, __) => const SizedBox(width: 8), itemBuilder: (context, i) { final isSelected = i == _selectedMonthIndex; return GestureDetector( onTap: () => setState(() => _selectedMonthIndex = i), child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), decoration: BoxDecoration( color: isSelected ? AppColors.primary : (isDark ? AppColors.surfaceDark : AppColors.surfaceLight), borderRadius: BorderRadius.circular(50), border: isSelected ? null : Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.2) : AppColors.cream, ), ), child: Center( child: Text( _months[i].label, style: TextStyle( fontSize: 13, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? AppColors.onPrimary : (isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight), ), ), ), ), ); }, ), ), const SizedBox(height: 12), // ── Location Card ── Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: GestureDetector( onTap: () => _showLocationDialog(context), child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(16), border: Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.1) : AppColors.cream, ), ), child: Row( children: [ const Icon(LucideIcons.mapPin, color: AppColors.primary, size: 24), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Lokasi Anda', style: theme.textTheme.bodySmall?.copyWith( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), Text( cityNameAsync.value ?? 'Jakarta, Indonesia', style: const TextStyle( fontWeight: FontWeight.w600, fontSize: 15), ), ], ), ), Icon(LucideIcons.chevronDown, color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight), ], ), ), ), ), const SizedBox(height: 16), // ── Table Header ── Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.1), borderRadius: const BorderRadius.vertical(top: Radius.circular(12)), ), child: Row( children: [ _headerCell('TGL', flex: 4), _headerCell('SUBUH', flex: 3), _headerCell('SYURUQ', flex: 3), _headerCell('DZUHUR', flex: 3), _headerCell('ASHAR', flex: 3), _headerCell('MAGH', flex: 3), _headerCell('ISYA', flex: 3), ], ), ), // ── Table Body ── Expanded( child: monthlyDataAsync.when( data: (apiData) { final rows = _createRows(apiData); return ListView.builder( padding: const EdgeInsets.fromLTRB( 16, 0, 16, tableBottomSpacing, ), itemCount: rows.length, itemBuilder: (context, i) { final row = rows[i]; final isToday = row.date.day == today.day && row.date.month == today.month && row.date.year == today.year; return Container( margin: const EdgeInsets.only(bottom: 4), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 14), decoration: BoxDecoration( color: isToday ? AppColors.primary : (isDark ? AppColors.surfaceDark : AppColors.surfaceLight), borderRadius: BorderRadius.circular(12), border: isToday ? null : Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.05) : AppColors.cream.withValues(alpha: 0.5), ), ), child: Row( children: [ // Day column Expanded( flex: 4, child: Column( children: [ Text( DateFormat('MMM') .format(row.date) .toUpperCase(), style: TextStyle( fontSize: 9, fontWeight: FontWeight.w700, letterSpacing: 1, color: isToday ? AppColors.onPrimary .withValues(alpha: 0.7) : AppColors.sage, ), ), Text( '${row.date.day}', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: isToday ? AppColors.onPrimary : null, ), ), ], ), ), _dataCell(row.fajr, isToday, flex: 3), _dataCell(row.sunrise, isToday, flex: 3), _dataCell(row.dhuhr, isToday, bold: true, flex: 3), _dataCell(row.asr, isToday, flex: 3), _dataCell(row.maghrib, isToday, bold: true, flex: 3), _dataCell(row.isha, isToday, flex: 3), ], ), ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (_, __) { final rows = _createRows(null); // fallback return ListView.builder( padding: const EdgeInsets.fromLTRB( 16, 0, 16, tableBottomSpacing, ), itemCount: rows.length, itemBuilder: (context, i) { final row = rows[i]; final isToday = row.date.day == today.day && row.date.month == today.month && row.date.year == today.year; return Container( margin: const EdgeInsets.only(bottom: 4), padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 14), decoration: BoxDecoration( color: isToday ? AppColors.primary : (isDark ? AppColors.surfaceDark : AppColors.surfaceLight), borderRadius: BorderRadius.circular(12), border: isToday ? null : Border.all( color: isDark ? AppColors.primary.withValues(alpha: 0.05) : AppColors.cream.withValues(alpha: 0.5), ), ), child: Row( children: [ Expanded( flex: 4, child: Column( children: [ Text( DateFormat('MMM') .format(row.date) .toUpperCase(), style: TextStyle( fontSize: 9, fontWeight: FontWeight.w700, letterSpacing: 1, color: isToday ? AppColors.onPrimary .withValues(alpha: 0.7) : AppColors.sage, ), ), Text( '${row.date.day}', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: isToday ? AppColors.onPrimary : null, ), ), ], ), ), _dataCell(row.fajr, isToday, flex: 3), _dataCell(row.sunrise, isToday, flex: 3), _dataCell(row.dhuhr, isToday, bold: true, flex: 3), _dataCell(row.asr, isToday, flex: 3), _dataCell(row.maghrib, isToday, bold: true, flex: 3), _dataCell(row.isha, isToday, flex: 3), ], ), ); }, ); }, ), ), ], ), ); } Widget _headerCell(String text, {int flex = 1}) { return Expanded( flex: flex, child: Center( child: Text( text, style: const TextStyle( fontSize: 9, fontWeight: FontWeight.w700, letterSpacing: 1, color: AppColors.textSecondaryLight, ), ), ), ); } Widget _dataCell(String value, bool isToday, {bool bold = false, int flex = 1}) { return Expanded( flex: flex, child: Center( child: Text( value, style: TextStyle( fontSize: 12, fontWeight: bold ? FontWeight.w700 : FontWeight.w400, color: isToday ? AppColors.onPrimary : null, ), ), ), ); } } class _MonthOption { final String label; final int month; final int year; _MonthOption({required this.label, required this.month, required this.year}); } class _DayRow { final DateTime date; final String fajr, sunrise, dhuhr, asr, maghrib, isha; _DayRow({ required this.date, required this.fajr, required this.sunrise, required this.dhuhr, required this.asr, required this.maghrib, required this.isha, }); }