import 'dart:io';
import 'package:flutter/material.dart' show TimeOfDay;
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'package:flutter_riverpod/flutter_riverpod.dart';

/// Provider for the [NotificationService].
final notificationServiceProvider = Provider<NotificationService>((ref) {
  return NotificationService();
});

/// Service responsible for managing local notifications across Android, iOS, and Linux.
class NotificationService {
  final FlutterLocalNotificationsPlugin _notifications = FlutterLocalNotificationsPlugin();

  /// Initializes the notification system and requests necessary permissions.
  Future<void> init() async {
    tz.initializeTimeZones();
    
    const AndroidInitializationSettings initializationSettingsAndroid =
        AndroidInitializationSettings('@mipmap/ic_launcher');

    const DarwinInitializationSettings initializationSettingsDarwin =
        DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );

    const LinuxInitializationSettings initializationSettingsLinux =
        LinuxInitializationSettings(
      defaultActionName: 'Open notification',
    );

    const InitializationSettings initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsDarwin,
      linux: initializationSettingsLinux,
    );

    await _notifications.initialize(
      initializationSettings,
    );

    if (Platform.isAndroid) {
      final androidImplementation =
          _notifications.resolvePlatformSpecificImplementation<
              AndroidFlutterLocalNotificationsPlugin>();
      await androidImplementation?.requestNotificationsPermission();
      await androidImplementation?.requestExactAlarmsPermission();
    }
  }

  /// Shows a notification immediately indicating that a rest period has finished.
  Future<void> showRestFinishedNotification({String? title, String? body}) async {
    const AndroidNotificationDetails androidPlatformChannelSpecifics =
        AndroidNotificationDetails(
      'rest_timer_channel',
      'Rest Timer Notifications',
      channelDescription: 'Notifications when the rest timer finishes',
      importance: Importance.max,
      priority: Priority.high,
      showWhen: true,
      enableVibration: true,
      playSound: true,
    );

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(
      android: androidPlatformChannelSpecifics,
      linux: LinuxNotificationDetails(),
    );

    await _notifications.show(
      1001,
      title ?? 'Rest Finished',
      body ?? 'Get back to your workout!',
      platformChannelSpecifics,
    );
  }

  /// Schedules recurring notifications for specified training days at a given time.
  /// 
  /// Fallbacks to immediate notification on Linux as [zonedSchedule] is not implemented.
  Future<void> scheduleDailyTrainingReminder({
    required List<String> trainingDays,
    required TimeOfDay reminderTime,
    String? title,
    String? body,
  }) async {
    await _notifications.cancel(1002);

    if (trainingDays.isEmpty) return;

    if (Platform.isLinux) {
      await _notifications.show(
        1002,
        title ?? 'Daily Reminder Configured',
        body ?? 'Training reminders are set for: ${trainingDays.join(', ')}',
        const NotificationDetails(linux: LinuxNotificationDetails()),
      );
      return;
    }

    const AndroidNotificationDetails androidPlatformChannelSpecifics =
        AndroidNotificationDetails(
      'daily_training_channel',
      'Daily Training Reminders',
      channelDescription: 'Reminders for scheduled training days',
      importance: Importance.max,
      priority: Priority.high,
    );

    const NotificationDetails platformChannelSpecifics =
        NotificationDetails(
      android: androidPlatformChannelSpecifics,
      linux: LinuxNotificationDetails(),
    );

    for (int i = 0; i < trainingDays.length; i++) {
      final day = trainingDays[i];
      final weekday = _getWeekdayNumber(day);
      if (weekday == null) continue;

      await _notifications.zonedSchedule(
        1002 + i,
        title ?? 'Training Day!',
        body ?? 'Today is a scheduled training day. Time to hit the iron!',
        _nextInstanceOfDay(weekday, reminderTime),
        platformChannelSpecifics,
        androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
        uiLocalNotificationDateInterpretation:
            UILocalNotificationDateInterpretation.absoluteTime,
        matchDateTimeComponents: DateTimeComponents.dayOfWeekAndTime,
      );
    }
  }

  int? _getWeekdayNumber(String day) {
    switch (day.toLowerCase()) {
      case 'mo':
      case 'monday':
        return DateTime.monday;
      case 'tu':
      case 'tuesday':
        return DateTime.tuesday;
      case 'we':
      case 'wednesday':
        return DateTime.wednesday;
      case 'th':
      case 'thursday':
        return DateTime.thursday;
      case 'fr':
      case 'friday':
        return DateTime.friday;
      case 'sa':
      case 'saturday':
        return DateTime.saturday;
      case 'su':
      case 'sunday':
        return DateTime.sunday;
      default:
        return null;
    }
  }

  tz.TZDateTime _nextInstanceOfDay(int weekday, TimeOfDay time) {
    tz.TZDateTime scheduledDate = _nextInstanceOfTime(time);
    while (scheduledDate.weekday != weekday) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }
    return scheduledDate;
  }

  tz.TZDateTime _nextInstanceOfTime(TimeOfDay time) {
    final tz.TZDateTime now = tz.TZDateTime.now(tz.local);
    tz.TZDateTime scheduledDate = tz.TZDateTime(
        tz.local, now.year, now.month, now.day, time.hour, time.minute);
    if (scheduledDate.isBefore(now)) {
      scheduledDate = scheduledDate.add(const Duration(days: 1));
    }
    return scheduledDate;
  }

  /// Cancels all active and scheduled notifications.
  Future<void> cancelAllNotifications() async {
    await _notifications.cancelAll();
  }
}

/// Simple time representation for scheduling notifications.
class TimeOfDay {
  final int hour;
  final int minute;
  const TimeOfDay({required this.hour, required this.minute});
}
