import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/services/dialog_service.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';

// ==================== Hijri Date Classes ====================

/// Represents a Hijri date with day, month, and year components.
class HijriGregDate {
  final int day;
  final int month;
  final int year;

  HijriGregDate({required this.day, required this.month, required this.year})
      : assert(day >= 1 && day <= 30, 'Day must be between 1 and 30'),
        assert(month >= 1 && month <= 12, 'Month must be between 1 and 12'),
        assert(year > 0, 'Year must be positive');

  @override
  String toString() => '$day/$month/$year H';

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    return other is HijriGregDate && other.day == day && other.month == month && other.year == year;
  }

  @override
  int get hashCode => day.hashCode ^ month.hashCode ^ year.hashCode;
}

/// Utility class for converting between Hijri and Gregorian calendars.
class HijriGregConverter {
  static const int _hijriEpoch = 1948439;

  static HijriGregDate gregorianToHijri(DateTime gregorianDate) {
    int julianDay = _gregorianToJulian(gregorianDate);
    return _julianToHijri(julianDay);
  }

  static DateTime hijriToGregorian(HijriGregDate hijriDate) {
    int julianDay = _hijriToJulian(hijriDate);
    return _julianToGregorian(julianDay);
  }

  static int _gregorianToJulian(DateTime date) {
    int year = date.year;
    int month = date.month;
    int day = date.day;

    if (month <= 2) {
      year--;
      month += 12;
    }

    int a = year ~/ 100;
    int b = 2 - a + (a ~/ 4);

    return (365.25 * (year + 4716)).floor() + (30.6001 * (month + 1)).floor() + day + b - 1524;
  }

  static DateTime _julianToGregorian(int julianDay) {
    int a = julianDay + 32044;
    int b = (4 * a + 3) ~/ 146097;
    int c = a - (146097 * b) ~/ 4;
    int d = (4 * c + 3) ~/ 1461;
    int e = c - (1461 * d) ~/ 4;
    int m = (5 * e + 2) ~/ 153;

    int day = e - (153 * m + 2) ~/ 5 + 1;
    int month = m + 3 - 12 * (m ~/ 10);
    int year = 100 * b + d - 4800 + m ~/ 10;

    return DateTime(year, month, day);
  }

  static HijriGregDate _julianToHijri(int julianDay) {
    int daysSinceEpoch = julianDay - _hijriEpoch;
    int hijriYear = (daysSinceEpoch * 33 ~/ 10631) + 1;

    int yearStartJulian = _hijriYearStartJulian(hijriYear);
    while (yearStartJulian > julianDay) {
      hijriYear--;
      yearStartJulian = _hijriYearStartJulian(hijriYear);
    }
    while (yearStartJulian + _hijriYearLength(hijriYear) <= julianDay) {
      hijriYear++;
      yearStartJulian = _hijriYearStartJulian(hijriYear);
    }

    int dayOfYear = julianDay - yearStartJulian + 1;
    int month = 1;
    int dayInMonth = dayOfYear;

    while (month <= 12) {
      int monthLength = _hijriMonthLength(hijriYear, month);
      if (dayInMonth <= monthLength) {
        break;
      }
      dayInMonth -= monthLength;
      month++;
    }

    if (dayInMonth < 1) dayInMonth = 1;
    if (dayInMonth > 30) dayInMonth = 30;

    return HijriGregDate(day: dayInMonth, month: month, year: hijriYear);
  }

  static int _hijriToJulian(HijriGregDate hijriDate) {
    int yearStart = _hijriYearStartJulian(hijriDate.year);
    int dayOfYear = 0;

    for (int i = 1; i < hijriDate.month; i++) {
      dayOfYear += _hijriMonthLength(hijriDate.year, i);
    }

    dayOfYear += hijriDate.day - 1;
    return yearStart + dayOfYear;
  }

  static int _hijriYearStartJulian(int hijriYear) {
    if (hijriYear <= 1) return _hijriEpoch;

    // A Hijri year has 354 days, with 11 leap days in every 30-year
    // cycle. Calculate the completed years directly instead of iterating
    // from year 1 for every calendar cell.
    final completedYears = hijriYear - 1;
    final completedLeapDays = (3 + (11 * hijriYear)) ~/ 30;
    return _hijriEpoch + (completedYears * 354) + completedLeapDays;
  }

  static int _hijriYearLength(int year) {
    return _isHijriLeapYear(year) ? 355 : 354;
  }

  static int _hijriMonthLength(int year, int month) {
    if (month <= 0 || month > 12) return 30;

    const List<int> monthLengths = [30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29];
    int length = monthLengths[month - 1];

    if (month == 12 && _isHijriLeapYear(year)) {
      length = 30;
    }

    return length;
  }

  static bool _isHijriLeapYear(int year) {
    if (year <= 0) return false;
    const leapYears = [2, 5, 7, 10, 13, 16, 18, 21, 24, 26, 29];
    int yearInCycle = ((year - 1) % 30) + 1;
    return leapYears.contains(yearInCycle);
  }

  static int getHijriMonthLength(int year, int month) {
    return _hijriMonthLength(year, month);
  }

  static bool isHijriLeapYear(int year) {
    return _isHijriLeapYear(year);
  }
}

// ==================== Main Model ====================

enum CalendarType {
  gregorian,
  hijri,
}

class DateRangCalenderModel extends ChangeNotifier {
  final AppState appState;
  final ErrorHandlerService navigationService;
  final DialogService dialogService;

  // Static variable to persist calendar type preference across instances
  static CalendarType _persistedCalendarType = CalendarType.gregorian;
  static int _persistedTabIndex = 0;

  DateRangCalenderModel({required this.appState, required this.navigationService, required this.dialogService}) {
    // Initialize from persisted state
    _initializeFromPersistedState();
  }

  int _selectedTabIndex = 0;
  CalendarType _calendarType = CalendarType.gregorian;

  void _initializeFromPersistedState() {
    // Load from static persisted values
    _selectedTabIndex = _persistedTabIndex;
    _calendarType = _persistedCalendarType;
  }

  // Umm al-Qura calendar adjustment (in days)
  // This offset corrects the astronomical calculation to match the official Saudi calendar
  static const int _ummAlQuraOffset = -1;

  // Hijri month names in English (short form)
  static const List<String> _hijriMonthNamesShort = [
    'Muh',
    'Saf',
    'Rab I',
    'Rab II',
    'Jum I',
    'Jum II',
    'Raj',
    'Sha',
    'Ram',
    'Shaw',
    'Dhu Q',
    'Dhu H',
  ];

  // Hijri month names in English (full form) - public for month picker
  static const List<String> hijriMonthNamesEnglish = [
    'Muharram',
    'Safar',
    'Rabi\' I',
    'Rabi\' II',
    'Jumada I',
    'Jumada II',
    'Rajab',
    'Sha\'ban',
    'Ramadan',
    'Shawwal',
    'Dhu al-Qi\'dah',
    'Dhu al-Hijjah',
  ];

  // Hijri month names in Arabic (short form)
  static const List<String> _hijriMonthNamesShortArabic = [
    'محرم',
    'صفر',
    'ربيع الأول',
    'ربيع الثاني',
    'جمادى الأولى',
    'جمادى الثانية',
    'رجب',
    'شعبان',
    'رمضان',
    'شوال',
    'ذو القعدة',
    'ذو الحجة',
  ];

  // Hijri month names in Arabic (full form) - public for month picker
  static const List<String> hijriMonthNamesArabic = [
    'محرم',
    'صفر',
    'ربيع الأول',
    'ربيع الثاني',
    'جمادى الأولى',
    'جمادى الثانية',
    'رجب',
    'شعبان',
    'رمضان',
    'شوال',
    'ذو القعدة',
    'ذو الحجة',
  ];

  // Gregorian month names (short form)
  static const List<String> _gregorianMonthNames = [
    'Jan',
    'Feb',
    'Mar',
    'Apr',
    'May',
    'Jun',
    'Jul',
    'Aug',
    'Sep',
    'Oct',
    'Nov',
    'Dec',
  ];

  // Gregorian month names in English (full form) - public for month picker
  static const List<String> gregorianMonthNamesEnglish = [
    'January',
    'February',
    'March',
    'April',
    'May',
    'June',
    'July',
    'August',
    'September',
    'October',
    'November',
    'December',
  ];

  // Gregorian month names in Arabic
  static const List<String> _gregorianMonthNamesArabic = [
    'يناير',
    'فبراير',
    'مارس',
    'أبريل',
    'مايو',
    'يونيو',
    'يوليو',
    'أغسطس',
    'سبتمبر',
    'أكتوبر',
    'نوفمبر',
    'ديسمبر',
  ];

  // Gregorian month names in Arabic (full form) - public for month picker
  static const List<String> gregorianMonthNamesArabic = [
    'يناير',
    'فبراير',
    'مارس',
    'أبريل',
    'مايو',
    'يونيو',
    'يوليو',
    'أغسطس',
    'سبتمبر',
    'أكتوبر',
    'نوفمبر',
    'ديسمبر',
  ];

  // Weekday names in English (short form)
  static const List<String> _weekdayNamesEnglish = [
    'Sun',
    'Mon',
    'Tue',
    'Wed',
    'Thu',
    'Fri',
    'Sat',
  ];

  // Weekday names in Arabic (short form)
  static const List<String> _weekdayNamesArabic = [
    'أحد',
    'اثنين',
    'ثلاثاء',
    'أربعاء',
    'خميس',
    'جمعة',
    'سبت',
  ];

  int get getSelectedTabIndex => _selectedTabIndex;

  CalendarType get calendarType => _calendarType;

  set selectedTabIndex(int value) {
    _selectedTabIndex = value;
    _calendarType = value == 0 ? CalendarType.gregorian : CalendarType.hijri;
    notifyListeners();
  }

  void setTabIndex(int index) {
    if (_selectedTabIndex == index) return;

    _selectedTabIndex = index;
    _calendarType = index == 0 ? CalendarType.gregorian : CalendarType.hijri;
    // Persist the selection for next time the widget opens
    _persistCalendarTypePreference();
    notifyListeners();
  }

  void _persistCalendarTypePreference() {
    // Store the preference in static variables so it persists when widget reopens
    _persistedTabIndex = _selectedTabIndex;
    _persistedCalendarType = _calendarType;
  }

  bool get isGregorian => _calendarType == CalendarType.gregorian;

  bool get isHijri => _calendarType == CalendarType.hijri;

  /// Reset to Gregorian and clear persisted preference
  /// Call this only when explicitly needed (e.g., logout)
  void reset({bool force = false}) {
    if (force) {
      // Force reset - clear both instance and persisted state
      _selectedTabIndex = 0;
      _calendarType = CalendarType.gregorian;
      _persistedTabIndex = 0;
      _persistedCalendarType = CalendarType.gregorian;
      notifyListeners();
    }
    // If not forcing, keep the user's preference (persisted state remains)
  }

  /// Clear persisted calendar type preference (reset to Gregorian on next open)
  static void clearPersistedPreference() {
    _persistedTabIndex = 0;
    _persistedCalendarType = CalendarType.gregorian;
  }

  // ==================== Conversion Methods ====================

  /// Converts Gregorian DateTime to Hijri date with Umm al-Qura correction
  HijriGregDate gregorianToHijri(DateTime gregorianDate) {
    // Apply Umm al-Qura offset for accurate Saudi calendar
    final adjustedDate = gregorianDate.add(Duration(days: _ummAlQuraOffset));
    return HijriGregConverter.gregorianToHijri(adjustedDate);
  }

  /// Converts Hijri date to Gregorian DateTime with Umm al-Qura correction
  DateTime hijriToGregorian(HijriGregDate hijriDate) {
    final gregorianDate = HijriGregConverter.hijriToGregorian(hijriDate);
    // Reverse the Umm al-Qura offset
    return gregorianDate.subtract(Duration(days: _ummAlQuraOffset));
  }

  // ==================== Date Formatting Methods ====================

  /// Formats a date based on the current calendar type
  /// Returns formatted string like "14 Apr,26" for Gregorian or "14 Muh,1447" for Hijri
  String formatDate(DateTime? date) {
    if (date == null) return "-";

    if (isGregorian) {
      return formatGregorianDate(date);
    } else {
      return formatHijriDate(date);
    }
  }

  /// Formats a Gregorian date
  /// Format: "dd MMM,yy" (e.g., "14 Apr,26")
  String formatGregorianDate(DateTime date) {
    String year = date.year.toString().substring(2);
    String month = _gregorianMonthNames[date.month - 1];
    String day = date.day.toString();
    return '$day $month,$year';
  }

  /// Formats a date as Hijri
  /// Format: "dd MMM,yyyy" (e.g., "14 Muh,1447")
  String formatHijriDate(DateTime gregorianDate) {
    HijriGregDate hijriDate = gregorianToHijri(gregorianDate);
    String month = _hijriMonthNamesShort[hijriDate.month - 1];
    return '${hijriDate.day} $month,${hijriDate.year}';
  }

  /// Gets month name based on current calendar type
  String getMonthName(int month) {
    if (isGregorian) {
      return _gregorianMonthNames[month - 1];
    } else {
      return _hijriMonthNamesShort[month - 1];
    }
  }

  /// Gets month name in Arabic based on current calendar type
  String getMonthNameArabic(int month) {
    if (isGregorian) {
      return _gregorianMonthNamesArabic[month - 1];
    } else {
      return _hijriMonthNamesShortArabic[month - 1];
    }
  }

  /// Gets month name with language support
  String getMonthNameLocalized(int month, bool isArabic) {
    return isArabic ? getMonthNameArabic(month) : getMonthName(month);
  }

  /// Gets weekday name in English
  String getWeekdayName(int weekday) {
    // weekday: 1 = Monday, 7 = Sunday in DateTime
    // Convert to Sunday = 0 index
    int index = weekday % 7;
    return _weekdayNamesEnglish[index];
  }

  /// Gets weekday name in Arabic
  String getWeekdayNameArabic(int weekday) {
    // weekday: 1 = Monday, 7 = Sunday in DateTime
    // Convert to Sunday = 0 index
    int index = weekday % 7;
    return _weekdayNamesArabic[index];
  }

  /// Gets weekday name with language support
  String getWeekdayNameLocalized(int weekday, bool isArabic) {
    return isArabic ? getWeekdayNameArabic(weekday) : getWeekdayName(weekday);
  }

  /// Converts a date string to display format based on calendar type
  String formatDateString(String? dateString) {
    if (dateString == null || dateString.isEmpty) return "-";

    try {
      DateTime date = DateTime.parse(dateString);
      return formatDate(date);
    } catch (e) {
      return "-";
    }
  }

  // ==================== Date Range Methods ====================

  /// Formats a date range based on current calendar type
  /// Returns formatted string like "14 Apr,26 - 20 Apr,26" for Gregorian
  String formatDateRange(DateTime? startDate, DateTime? endDate) {
    if (startDate == null || endDate == null) return "-";

    String start = formatDate(startDate);
    String end = formatDate(endDate);
    return '$start - $end';
  }

  /// Gets the current date in the selected calendar format
  String getCurrentDateFormatted() {
    return formatDate(DateTime.now());
  }

  // ==================== Calendar Type Specific Helpers ====================

  /// Gets the year based on calendar type
  int getCurrentYear() {
    if (isGregorian) {
      return DateTime.now().year;
    } else {
      return gregorianToHijri(DateTime.now()).year;
    }
  }

  /// Gets formatted year string based on calendar type
  String getFormattedYear(DateTime date) {
    if (isGregorian) {
      return date.year.toString();
    } else {
      return gregorianToHijri(date).year.toString();
    }
  }

  /// Checks if a year is leap year based on calendar type
  bool isLeapYear(int year) {
    if (isGregorian) {
      return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
    } else {
      return HijriGregConverter.isHijriLeapYear(year);
    }
  }

  /// Gets the number of days in a month based on calendar type
  int getDaysInMonth(int year, int month) {
    if (isGregorian) {
      return DateTime(year, month + 1, 0).day;
    } else {
      return HijriGregConverter.getHijriMonthLength(year, month);
    }
  }
}
