import 'dart:async';

import 'package:device_calendar_plus/device_calendar_plus.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:jiffy/jiffy.dart' show Jiffy;
import 'package:manage_calendar_events/manage_calendar_events.dart' hide Calendar;
import 'package:permission_handler/permission_handler.dart';

class CalenderUtilsNew {
  final DeviceCalendar calender = DeviceCalendar.instance;
  List<Calendar> writableCalender = [];

  /// Completer to ensure getCalenders() only runs once at a time
  Completer<void>? _calendarInitCompleter;

  CalenderUtilsNew._instance();

  static final CalenderUtilsNew instance = CalenderUtilsNew._instance();

  /// Ensures calendars are loaded exactly once. Safe to call multiple times —
  /// subsequent calls await the same future.
  Future<void> ensureCalendarsLoaded() async {
    if (writableCalender.isNotEmpty) return;
    if (_calendarInitCompleter != null) {
      // Already in progress — just wait for it
      return _calendarInitCompleter!.future;
    }
    _calendarInitCompleter = Completer<void>();
    try {
      await _loadCalendars();
      _calendarInitCompleter!.complete();
    } catch (e) {
      _calendarInitCompleter!.completeError(e);
      _calendarInitCompleter = null; // Allow retry on failure
      rethrow;
    }
  }

  Future<void> _loadCalendars() async {
    CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions();
    if (result != CalendarPermissionStatus.granted) {
      await DeviceCalendar.instance.requestPermissions();
      result = await DeviceCalendar.instance.hasPermissions();
      if (result != CalendarPermissionStatus.granted) {
        print("the permission is not granted");
        showCommonBottomSheetWithoutHeight(
          title: LocaleKeys.notice.tr(context: GetIt
              .instance<NavigationService>()
              .navigatorKey
              .currentContext!),
          GetIt
              .instance<NavigationService>()
              .navigatorKey
              .currentContext!,
          child: Utils.getWarningWidget(
              loadingText: LocaleKeys.calendarPermissionAlert.tr(),
              isShowActionButtons: true,
              onCancelTap: () {
                GetIt.instance<NavigationService>().pop();
              },
              onConfirmTap: () async {
                GetIt.instance<NavigationService>().pop();
                openAppSettings();
              }),
          callBackFunc: () {},
          isFullScreen: false,
          isCloseButtonVisible: true,
        );
      }
    }
    var calenders = await calender.listCalendars();
    calenders.forEach((calender) {
      if (!calender.readOnly) {
        writableCalender.add(calender);
      }
    });
  }

  FutureOr<bool> createOrUpdateEvent({required String title, required String description, required String location, DateTime? scheduleDateTime, String? eventId, int? reminderMinutes}) async {
    await ensureCalendarsLoaded();
    var writableCalendars = writableCalender.first;
    String eventResult = "";
    // CalendarPermissionStatus result = await DeviceCalendar.instance.hasPermissions();
    // if (result != CalendarPermissionStatus.granted) await DeviceCalendar.instance.requestPermissions();
    // print(result);

    // String eventId = await getEventIdIfEventExist(title!.split("#").last);
    // if (eventId.isEmpty) {
    eventResult = await DeviceCalendar.instance.createEvent(
      calendarId: writableCalendars!.id,
      title: title,
      description: description,
      startDate: scheduleDateTime!,
      endDate: scheduleDateTime!.add(Duration(minutes: 30)),

      reminderMinutes: reminderMinutes, // TODO : NEED TO CONFIRM THIS FROM TAHA
    );
    return eventResult.isNotEmpty;
    // }

    // await DeviceCalendar.instance.updateEvent(
    //   eventId: eventId,
    //   title: title,
    //   description: description,
    //   startDate: scheduleDateTime!,
    //   endDate: scheduleDateTime!.add(Duration(minutes: 30)),
    // );

    // return eventId.isNotEmpty;
  }

  FutureOr<bool> createMultipleEvents({
    required int reminderMinutes,
    int? frequencyNumber,
    num scaleOffset = 24,
    required int days,
    required String orderDate,
    required String itemDescriptionN,
    required String route,
    Function(String)? onFailure,
    String? prescriptionNumber,
    DateTime? scheduleDateTime,
  }) async {
    DateTime startTimeOfDay = scheduleDateTime ?? DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day, 8, 0);
    print("the frequency is $frequencyNumber");
    print("the scaleOffset is $scaleOffset");

    frequencyNumber ??= 2; //Some time frequency number is null so by default will be 2

    // Calculate interval between doses in hours
    int intervalBetweenDoses = calculateIntervalAsPerFrequency(frequencyNumber, scaleOffset);

    // Calculate interval between cycles (days between each set of doses)
    // scaleOffset represents hours in one cycle (e.g., 24 for daily, 168 for weekly)
    int daysPerCycle = (scaleOffset / 24).ceil();  // 168 / 24 = 7 days


    print("Interval between doses: $intervalBetweenDoses hours");
    print("Days per cycle: $daysPerCycle");

    // Calculate remaining days based on prescription days and order date
    DateTime orderDateTime = DateUtil.convertStringToDate(orderDate);
    int daysSinceOrder = DateTime.now().difference(orderDateTime).inDays;
    int remainingDays = days - daysSinceOrder;

    // Validate that prescription is still active
    if (remainingDays <= 0) {
      onFailure?.call(LocaleKeys.prescriptionDatePassedError.tr());
      return false;
    }

    bool statusOfOperation = false;

    // Calculate number of cycles (e.g., for weekly medication over 90 days = ~13 weeks)
    int numberOfCycles = (remainingDays / daysPerCycle).ceil();  // 90 / 7 = 13 cycles

    print("Number of cycles: $numberOfCycles");
    print("Remaining days: $remainingDays");

    // Iterate through each cycle (daily, weekly, etc.)

    for (int i = 0; i < numberOfCycles; i++) {  // Loops 13 times (weekly)
      DateTime cycleStartDate = startTimeOfDay.add(Duration(days: i * daysPerCycle)); // Adds 7 days each time
      // Check if this cycle is still within the prescription period
      if (cycleStartDate.difference(orderDateTime).inDays > days) {
        break; // Stop if we've exceeded the prescription duration
      }

      DateTime actualDate = cycleStartDate;

      // Create events for each dose in this cycle (frequency per cycle)
      for (int j = 0; j < frequencyNumber; j++) {
        statusOfOperation = await createOrUpdateEvent(
            title: "$itemDescriptionN Medication about to due for prescription, #$prescriptionNumber",
            description: "$itemDescriptionN $frequencyNumber $route ",
            scheduleDateTime: actualDate,
            location: '',
            reminderMinutes: reminderMinutes);
        if (!statusOfOperation) return false;

        // Add interval between doses (in hours)
        actualDate = actualDate.add(Duration(hours: intervalBetweenDoses));
      }
    }

    return statusOfOperation;
  }

  int calculateIntervalAsPerFrequency(int frequencyNumber, num scaleOffset) {
    return scaleOffset ~/ frequencyNumber;
  }

  FutureOr<List<Event>> getEvents() async {
    var availableCalender =  writableCalender.first;
    DateTime startEventsDate = Jiffy.parseFromDateTime(DateTime.now()).subtract(days: 30).dateTime;
    DateTime endEventsDate = Jiffy.parseFromDateTime(DateTime.now()).add(days: 120).dateTime;
    return await calender.listEvents(startEventsDate, endEventsDate, calendarIds: [availableCalender.id]);
  }

  FutureOr<bool> checkIfEventExist(String admissionId) async {
    // await ensureCalendarsLoaded();
    if (writableCalender.isEmpty) {
      await ensureCalendarsLoaded();
    }
    List<Event> events = await getEvents();
    if (events.isEmpty) return false;
    for (var event in events) {
      List<String> title = event.title!.split("#");

      if (title.contains(admissionId)) return true;
    }
    return false;
  }

  FutureOr<String> getEventIdIfEventExist(String admissionId) async {
    if (writableCalender.isEmpty) {
      await ensureCalendarsLoaded();
    }
    List<Event> events = await getEvents();
    if (events.isEmpty) return "";
    for (var event in events) {
      List<String> title = event.title!.split("#");
      if (title.contains(admissionId)) return event.eventId;
    }
    return "";
  }

  FutureOr<bool> checkAndRemove({required String id}) async {
    if (writableCalender.isEmpty) {
      await ensureCalendarsLoaded();
    }
    List<Event> events = await getEvents();
    if (events.isEmpty) return false;
    for (var event in events) {
      List<String> title = event.title!.split("#");

      if (title.contains(id)) {
        calender.deleteEvent(eventId: event.eventId);
        return true;
      }
    }
    return false;
  }

  FutureOr<bool> checkAndRemoveMultipleItems({required String id}) async {
    if (writableCalender.isEmpty) {
      await ensureCalendarsLoaded();
    }
    List<Event> events = await getEvents();
    if (events.isEmpty) return false;
    bool statusOfOperation = false;
    for (var event in events) {
      List<String> title = event.title.split("#");

      if (title.contains(id)) {
        calender.deleteEvent(eventId: event.eventId);
        statusOfOperation = true;
      }
    }
    return statusOfOperation;
  }
}
