import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as dartMath;
import 'dart:typed_data';

import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:crypto/crypto.dart' as crypto;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:get_it/get_it.dart';
import 'package:google_api_availability/google_api_availability.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/privilege/ProjectDetailListModel.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.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/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.dart';
import 'package:hmg_patient_app_new/widgets/loading_dialog.dart';
import 'package:hmg_patient_app_new/widgets/accessibility/preserve_image_colors.dart';
import 'package:hmg_patient_app_new/widgets/accessibility/preserve_svg_colors.dart';
import 'package:lottie/lottie.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:url_launcher/url_launcher.dart';

class Utils {
  static AppState appState = getIt.get<AppState>();
  static NavigationService navigationService = getIt.get<NavigationService>();

  static bool _isLoadingVisible = false;

  static bool get isLoading => _isLoadingVisible;

  static var navigationProjectsList = [
    {
      "Desciption": "Sahafa Hospital",
      "DesciptionN": "مستشفى الصحافة",
      "ID": 1, // Campus ID
      "LegalName": "Sahafa Hospital",
      "LegalNameN": "مستشفى الصحافة",
      "Name": "Sahafa Hospital",
      "NameN": "مستشفى الصحافة",
      "PhoneNumber": "+966115222222",
      "SetupID": "013311",
      "DistanceInKilometers": 0,
      "HasVida3": false,
      "IsActive": true,
      "IsHmg": true,
      "IsVidaPlus": false,
      "Latitude": "24.8111548",
      "Longitude": "46.6217912",
      "MainProjectID": 130,
      "ProjectOutSA": false,
      "UsingInDoctorApp": false,
      "IsHMC": false
    },
    {
      "Desciption": "Jeddah Fayhaa Hospital",
      "DesciptionN": "مستشفى جدة الفيحاء",
      "ID": 3, // Campus ID
      "LegalName": "Jeddah Fayhaa Hospital",
      "LegalNameN": "مستشفى جدة الفيحاء",
      "Name": "Jeddah Fayhaa Hospital",
      "NameN": "مستشفى جدة الفيحاء",
      "PhoneNumber": "+966115222222",
      "SetupID": "013311",
      "DistanceInKilometers": 0,
      "HasVida3": false,
      "IsActive": true,
      "IsHmg": true,
      "IsVidaPlus": false,
      "Latitude": "21.5086703",
      "Longitude": "39.2121855",
      "MainProjectID": 130,
      "ProjectOutSA": false,
      "UsingInDoctorApp": false,
      "IsHMC": false
    }
  ];

  static void showToast(String message, {bool longDuration = true}) {
    Fluttertoast.showToast(
        msg: message,
        toastLength: longDuration ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT,
        gravity: ToastGravity.BOTTOM,
        timeInSecForIosWeb: 1,
        backgroundColor: AppColors.blackColor,
        textColor: AppColors.whiteColor,
        fontSize: 16.0);
  }

  static bool isArabicText(String inputText) {
    bool isArabicText = false;
    // Updated regex to include:
    // - Arabic letters: \u0621-\u064A
    // - Arabic numerals: \u0660-\u0669 (٠-٩)
    // - Persian numerals: \u06F0-\u06F9 (۰-۹)
    final arabic = RegExp(r'[\u0621-\u064A\u0660-\u0669\u06F0-\u06F9]');
    if (arabic.hasMatch(inputText)) {
      isArabicText = true;
    } else {
      isArabicText = false;
    }

    return isArabicText;
  }

  static String getFreeSlotsTimeText(String startTime, {bool isAddHours = false}) {
    // return DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime)!.add(
    //   Duration(
    //     hours: isAddHours ? 3 : 0,
    //   ),
    // ));
    return !isAddHours
        ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal())
        : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add(
            Duration(
              hours: isAddHours ? 3 : 0,
            ),
          ));
  }

  static String convertStringToDateTime(String dateTimeString) {
    String timeString = dateTimeString;
    // Parse the time string using DateFormat
    DateFormat format = DateFormat.Hms(["en-US"]); // 'Hms' = 'HH:mm:ss'
    DateTime time = format.parse(timeString);

    DateTime now = DateTime.now();
    DateTime dateTimeWithToday = DateTime(
      now.year,
      now.month,
      now.day,
      time.hour,
      time.minute,
      time.second,
    );

    return dateTimeWithToday.toIso8601String();
  }

  static String getMonthDayYearDateFormatted(DateTime dateTime) {
    return appState.isArabic()
        ? getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString()
        : getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString();
  }

  static String getDayMonthYearDateFormatted(DateTime? dateTime) {
    if (dateTime == null) return "";
    return
        // appState.isArabic()
        //   ? "${dateTime.day.toString()} ${getMonthArabic(dateTime.month)}, ${dateTime.year.toString()}"
        //   :
        "${dateTime.day.toString()} ${getMonth(dateTime.month)}, ${dateTime.year.toString()}";
  }

  /// get month by
  /// [month] convert month number in to month name
  static getMonth(int month) {
    switch (month) {
      case 1:
        return "January";
      case 2:
        return "February";
      case 3:
        return "March";
      case 4:
        return "April";
      case 5:
        return "May";
      case 6:
        return "June";
      case 7:
        return "July";
      case 8:
        return "August";
      case 9:
        return "September";
      case 10:
        return "October";
      case 11:
        return "November";
      case 12:
        return "December";
    }
  }

  /// get month by
  /// [month] convert month number in to month name in Arabic
  static getMonthArabic(int month) {
    switch (month) {
      case 1:
        return "يناير";
      case 2:
        return " فبراير";
      case 3:
        return "مارس";
      case 4:
        return "أبريل";
      case 5:
        return "مايو";
      case 6:
        return "يونيو";
      case 7:
        return "يوليو";
      case 8:
        return "أغسطس";
      case 9:
        return "سبتمبر";
      case 10:
        return " اكتوبر";
      case 11:
        return " نوفمبر";
      case 12:
        return "ديسمبر";
    }
  }

  static Future<bool> getBoolFromPrefs(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getBool(key) ?? true;
  }

  static Future<bool> saveBoolFromPrefs(String key, bool value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setBool(key, value);
  }

  static Future<String> getStringFromPrefs(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getString(key) ?? "";
  }

  static Future<bool> saveStringFromPrefs(String key, String value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setString(key, value);
  }

  static Future<int> getIntFromPrefs(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return prefs.getInt(key) ?? 0;
  }

  static Future<bool> saveIntFromPrefs(String key, int value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setInt(key, value);
  }

  static Future<num> getNumFromPrefs(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return num.parse(prefs.getString(key) ?? "0");
  }

  static Future<bool> saveNumFromPrefs(String key, num value) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.setString(key, value.toString());
  }

  static Future<bool> removeFromPrefs(String key) async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    return await prefs.remove(key);
  }

  static void showLoadingDialog() {
    _isLoadingVisible = true;
    showDialog(
      context: navigationService.navigatorKey.currentContext!,
      barrierColor: AppColors.blackColor,
      builder: (BuildContext context) => LoadingDialog(),
    )
        .then((value) {
          _isLoadingVisible = false;
        })
        .catchError((e) {})
        .onError(
          (error, stackTrace) {},
        );
  }

  static List<T> uniqueBy<T, K>(List<T> list, K Function(T) keySelector) {
    final seenKeys = <K>{};
    return list.where((item) => seenKeys.add(keySelector(item))).toList();
  }

  static void showAppDialog(BuildContext context, String? title, String? message, VoidCallback? onTap) {
    showDialog(
      barrierDismissible: false,
      context: context,
      builder: (cxt) => ConfirmDialog(
        title: title!,
        message: message!,
        onTap: onTap,
      ),
    );
  }

  static bool isSAUDIIDValid(
    String id,
  ) {
    try {
      id = id.toString();
      id = id.trim();
      var returnValue = int.parse(id);
      var sum = 0;
      if (returnValue > 0) {
        var type = int.parse(id[0]);

        if (id.length != 10) {
          return false;
        }
        if (type != 2 && type != 1) {
          return false;
        }

        for (var i = 0; i < 10; i++) {
          if (i % 2 == 0) {
            var a = id[i];
            var x = int.parse(a) * 2;
            var b = x.toString();
            if (b.length == 1) {
              b = "0$b";
            }
            sum += int.parse(b[0]) + int.parse(b[1]);
          } else {
            sum += int.parse(id[i]);
          }
        }
        return sum % 10 == 0;
      }
    } catch (err) {
      log("errr: ${err.toString()}");
    }
    return false;
  }

  static Widget getNoDataWidget(
    BuildContext context, {
    double width = 124,
    double height = 124,
    String? noDataText,
    Widget callToActionButton = const SizedBox.shrink(),
    bool isSmallWidget = false,
  }) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        SizedBox(height: isSmallWidget ? 0.h : 48.h),
        Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
        SizedBox(height: 16.h),
        (noDataText ?? LocaleKeys.noDataAvailable.tr()).toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true).paddingSymmetrical(64.w, 0.h),
        SizedBox(height: 16.h),
        callToActionButton
      ],
    ).center;
  }

  static Widget getLoadingWidget({String? loadingText}) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill),
        SizedBox(height: 8.h),
        (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true),
        SizedBox(height: 8.h),
      ],
    ).center;
  }

  static Widget getSuccessWidget({String? loadingText, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center}) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: crossAxisAlignment,
      children: [
        Lottie.asset(AppAnimations.checkmark, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
        SizedBox(height: 8.h),
        (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isBold: true).paddingSymmetrical(24.h, 0.h),
        SizedBox(height: 8.h),
      ],
    ).center;
  }

  static Widget getErrorWidget({String? loadingText}) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
        SizedBox(height: 8.h),
        (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h),
        SizedBox(height: 8.h),
      ],
    ).center;
  }

  static Widget getOnlineCheckInWidget({
    String? loadingText,
    bool isShowActionButtons = false,
    bool showOkButton = false,
    Widget? bodyWidget,
    Function? onConfirmTap,
    Function? onCancelTap,
    String? cancelText,
    String? confirmText,
  }) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Utils.buildSvgWithAssets(icon: AppAssets.online_checkin_icon, width: 80.h, height: 80.h, applyThemeColor: false),
        SizedBox(height: 16.h),
        (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, letterSpacing: 0, weight: FontWeight.w600),
        SizedBox(height: 16.h),
        bodyWidget ?? SizedBox.shrink(),
        SizedBox(height: 16.h),
        isShowActionButtons
            ? Row(
                children: [
                  Expanded(
                    child: CustomButton(
                      text: cancelText ?? LocaleKeys.cancel.tr(),
                      onPressed: () {
                        if (onCancelTap != null) {
                          onCancelTap();
                        }
                      },
                      backgroundColor: AppColors.primaryRedColor,
                      borderColor: AppColors.primaryRedColor,
                      textColor: AppColors.whiteColor,
                      // icon: AppAssets.cancel,
                      // iconColor: AppColors.whiteColor,
                    ),
                  ),
                  SizedBox(width: 8.h),
                  Expanded(
                    child: CustomButton(
                      text: confirmText ?? LocaleKeys.confirm.tr(),
                      onPressed: () async {
                        if (onConfirmTap != null) {
                          onConfirmTap();
                        }
                      },
                      backgroundColor: AppColors.bgGreenColor,
                      borderColor: AppColors.bgGreenColor,
                      textColor: Colors.white,
                      // icon: AppAssets.confirm,
                    ),
                  ),
                ],
              )
            : showOkButton
                ? Row(
                    children: [
                      Expanded(
                        child: CustomButton(
                          text: LocaleKeys.ok.tr(),
                          onPressed: () async {
                            if (onConfirmTap != null) {
                              onConfirmTap();
                            }
                          },
                          backgroundColor: AppColors.bgGreenColor,
                          borderColor: AppColors.bgGreenColor,
                          textColor: Colors.white,
                          // icon: AppAssets.confirm,
                        ),
                      ),
                    ],
                  )
                : SizedBox.shrink(),
      ],
    ).center;
  }

  static Widget getWarningWidget({
    String? loadingText,
    bool isShowActionButtons = false,
    bool showOkButton = false,
    Widget? bodyWidget,
    Function? onConfirmTap,
    Function? onCancelTap,
    String? cancelText,
    String? confirmText,
  }) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
        SizedBox(height: 8.h),
        (loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0),
        SizedBox(height: 16.h),
        bodyWidget ?? SizedBox.shrink(),
        SizedBox(height: 16.h),
        isShowActionButtons
            ? Row(
                children: [
                  Expanded(
                    child: CustomButton(
                      text: cancelText ?? LocaleKeys.cancel.tr(),
                      onPressed: () {
                        if (onCancelTap != null) {
                          onCancelTap();
                        }
                      },
                      backgroundColor: AppColors.primaryRedColor,
                      borderColor: AppColors.primaryRedColor,
                      textColor: AppColors.whiteColor,
                      // icon: AppAssets.cancel,
                      // iconColor: AppColors.whiteColor,
                    ),
                  ),
                  SizedBox(width: 8.h),
                  Expanded(
                    child: CustomButton(
                      text: confirmText ?? LocaleKeys.confirm.tr(),
                      onPressed: () async {
                        if (onConfirmTap != null) {
                          onConfirmTap();
                        }
                      },
                      backgroundColor: AppColors.bgGreenColor,
                      borderColor: AppColors.bgGreenColor,
                      textColor: Colors.white,
                      // icon: AppAssets.confirm,
                    ),
                  ),
                ],
              )
            : showOkButton
                ? Row(
                    children: [
                      Expanded(
                        child: CustomButton(
                          text: LocaleKeys.ok.tr(),
                          onPressed: () async {
                            if (onConfirmTap != null) {
                              onConfirmTap();
                            }
                          },
                          backgroundColor: AppColors.bgGreenColor,
                          borderColor: AppColors.bgGreenColor,
                          textColor: Colors.white,
                          // icon: AppAssets.confirm,
                        ),
                      ),
                    ],
                  )
                : SizedBox.shrink(),
      ],
    ).center;
  }

  static bool isVidaPlusProject(int projectID) {
    AppState appState = getIt.get<AppState>();
    bool isVidaPlus = false;
    for (var element in appState.vidaPlusProjectList) {
      if (element.projectID == projectID) {
        isVidaPlus = true;
      }
    }
    return isVidaPlus;
  }

  static ProjectDetailListModel getProjectDetailObj(AppState appState, int projectID) {
    ProjectDetailListModel projectDetailListModel = ProjectDetailListModel();
    for (var element in appState.projectDetailListModel) {
      if (element.projectID == projectID) {
        projectDetailListModel = element;
      }
    }
    return projectDetailListModel;
  }

  static double distance(double lat1, double lon1, double lat2, double lon2) {
    const r = 6372.8; // Earth radius in kilometers

    final dLat = _toRadians(lat2 - lat1);
    final dLon = _toRadians(lon2 - lon1);
    final lat1Radians = _toRadians(lat1);
    final lat2Radians = _toRadians(lat2);

    final a = _haversin(dLat) + dartMath.cos(lat1Radians) * dartMath.cos(lat2Radians) * _haversin(dLon);
    final c = 2 * dartMath.asin(dartMath.sqrt(a));

    return r * c;
  }

  static double _toRadians(double degrees) => degrees * dartMath.pi / 180;

  static num _haversin(double radians) => dartMath.pow(dartMath.sin(radians / 2), 2);

  static getPhoneNumberWithoutZero(String number) {
    String newNumber = "";
    if (number.startsWith('0')) {
      newNumber = number.substring(1);
    } else {
      newNumber = number;
    }
    return newNumber;
  }

  static String removeHtmlTags(String htmlString) {
    if (htmlString.isEmpty) {
      return '';
    }

// Replace HTML line breaks with newlines
    var withLineBreaks =
        htmlString.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'<divider>', multiLine: true), '\n');

// Remove all other HTML tags
    var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), '');

// Decode HTML entities
    var decodedString = withoutTags
        .replaceAll('&nbsp;', ' ')
        .replaceAll('&amp;', '&')
        .replaceAll('&lt;', '<')
        .replaceAll('&gt;', '>')
        .replaceAll('&quot;', '"')
        .replaceAll('&#39;', "'")
        .replaceAll('&rsquo;', "'")
        .replaceAll('&lsquo;', "'")
        .replaceAll('&rdquo;', '"')
        .replaceAll('&ldquo;', '"');

// Remove extra whitespace and normalize line breaks
    var normalizedString = decodedString
        .replaceAll(RegExp(r'\n\s*\n'), '\n\n') // Replace multiple blank lines with double line break
        .replaceAll(RegExp(r' +'), ' ') // Replace multiple spaces with single space
        .trim(); // Remove leading/trailing whitespace

    return normalizedString;
  }

  Widget mDivider(Color color) {
    return Divider(
// width: double.infinity,
      height: 1,
      color: color,
    );
  }

// New Ui Items

  static String formatDateToDisplay(String isoDateString) {
    try {
      final dateTime = DateTime.parse(isoDateString);
      final day = dateTime.day.toString().padLeft(2, '0');
      final year = dateTime.year.toString();

// Map month number to short month name
      const monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
      final month = monthNames[dateTime.month - 1];

      return '$day $month, $year';
    } catch (e) {
      log("Error formatting date: $e");
      return "";
    }
  }

  static String formatDateForApi(String isoDateString) {
    try {
      final dateTime = DateTime.parse(isoDateString);
      final year = dateTime.year.toString();
      final month = dateTime.month.toString().padLeft(2, '0');
      final day = dateTime.day.toString().padLeft(2, '0');
      return '$day/$month/$year';
    } catch (e) {
      log("Error formatting date for API: $e");
      return "";
    }
  }

  static String formatHijriDateToDisplay(String hijriDateString) {
    try {
// Assuming hijriDateString is in the format yyyy-MM-dd
      final datePart = hijriDateString.split("T").first;
      final parts = datePart.split('-');
      if (parts.length != 3) return "";

      final day = parts[2].padLeft(2, '0');
      final year = parts[0];

// Map month number to short month name (Hijri months)
      const hijriMonthNames = ['Muharram', 'Safar', 'Rabi I', 'Rabi II', 'Jumada I', 'Jumada II', 'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'];
      final monthIndex = int.tryParse(parts[1]) ?? 1;
      final month = hijriMonthNames[monthIndex - 1];

      return '$day $month, $year';
    } catch (e) {
      log("Error formatting hijri date: $e");
      return "";
    }
  }

  static String formatDateToUsage(String isoDateString) {
    try {
      final dateTime = DateTime.parse(isoDateString);
      final day = dateTime.day.toString().padLeft(2, '0');
      final month = dateTime.month.toString().padLeft(2, '0');
      final year = dateTime.year.toString();

      return '$day-$month-$year';
    } catch (e) {
      log("Error formatting date: $e");
      return "";
    }
  }

  static Widget showLottie({
    required BuildContext context,
    String assetPath = 'assets/icons/no_property.json',
    double? height,
    double? width,
    BoxFit fit = BoxFit.cover,
    Alignment alignment = Alignment.center,
    bool repeat = false,
    bool animate = true,
    void Function(LottieComposition)? onLoaded,
  }) {
    return Lottie.asset(assetPath,
        height: height ?? MediaQuery.of(context).size.height * 0.26,
        width: width ?? MediaQuery.of(context).size.width,
        fit: fit,
        alignment: alignment,
        repeat: repeat,
        animate: animate,
        onLoaded: onLoaded);
  }

  static Widget buildSvgWithAssets({
    required String icon,
    Color? iconColor,
    bool isDisabled = false,
    double width = 24,
    double height = 24,
    BoxFit fit = BoxFit.cover,
    bool applyThemeColor = true,
    String? errorAsset,
  }) {
    final Color? resolvedColor = iconColor ?? (applyThemeColor && AppColors.isDarkMode ? AppColors.textColor : null);
    return PreserveSvgColors(
      child: SvgPicture.asset(
        icon,
        colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null,
        width: width,
        height: height,
        fit: fit,
        errorBuilder: errorAsset != null
            ? (context, error, stackTrace) => SvgPicture.asset(
                  errorAsset,
                  colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null,
                  width: width,
                  height: height,
                  fit: fit,
                )
            : null,
      ),
    );
  }

  /// Widget to build an SVG from network
  static Widget buildSvgWithNetwork({
    required String url,
    required Color iconColor,
    bool isDisabled = false,
    double? width,
    double? height,
  }) {
    final iconH = height ?? 24.h;
    final iconW = width ?? 24.w;
    return PreserveSvgColors(
      child: SvgPicture.network(
        url,
        colorFilter: ColorFilter.mode(
          isDisabled ? iconColor.withValues(alpha: 0.5) : iconColor,
          BlendMode.srcIn,
        ),
        width: iconW,
        height: iconH,
      ),
    );
  }

  static Widget buildImgWithAssets({
    required String icon,
    Color? iconColor,
    bool isDisabled = false,
    double? width,
    double? height,
    BoxFit fit = BoxFit.cover,
    double? border,
    double? borderRadius,
  }) {
    final iconH = height ?? 24.h;
    final iconW = width ?? 24.w;
    return PreserveImageColors(
      child: Container(
        decoration: BoxDecoration(
          border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null,
          borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null,
        ),
        child: Image.asset(icon, width: iconW, height: iconH, fit: fit),
      ),
    );
  }

  static Widget buildImgWithAssetsWithBorderRadius({
    required String icon,
    Color? iconColor,
    bool isDisabled = false,
    double? width,
    double? height,
    BoxFit fit = BoxFit.cover,
    double? border,
    double? borderRadius,
  }) {
    final iconH = height ?? 24.h;
    final iconW = width ?? 24.w;
    return PreserveImageColors(
      child: Container(
        width: iconW,
        height: iconH,
        decoration: BoxDecoration(
            border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null,
            borderRadius: BorderRadius.circular(borderRadius ?? 12.r),
            image: DecorationImage(
                image: AssetImage(
                  icon,
                ),
                fit: fit)),
      ),
    );
  }

  /// Widget to build an image from network - automatically preserves colors in visual modes
  static Widget buildImgWithNetwork({
    required String url,
    required Color iconColor,
    bool isDisabled = false,
    double? width,
    double? height,
    BoxFit fit = BoxFit.cover,
    ImageErrorWidgetBuilder? errorBuilder,
  }) {
    final iconH = height ?? 24.h;
    final iconW = width ?? 24.w;
    return PreserveImageColors(
      child: Image.network(
        url,
        width: iconW,
        height: iconH,
        fit: fit,
        errorBuilder: errorBuilder ??
            (_, __, ___) {
              //todo_section change the error builder icon that it is returning
              return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon);
            },
      ),
    );
  }

  static Widget getPaymentMethods({num spacing = 3, num width = 35, num height = 35}) {
    return Row(
      spacing: spacing.w,
      mainAxisSize: MainAxisSize.max,
      children: [
        PreserveImageColors(
          child: Image.asset(AppAssets.mada, width: width.h, height: height.h),
        ),
        PreserveImageColors(
          child: Image.asset(
            AppAssets.tamaraEng,
            width: width.h,
            height: height.h,
            fit: BoxFit.contain,
            errorBuilder: (context, error, stackTrace) {
              debugPrint('Failed to load Tamara PNG in payment methods: $error');
              return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 35.h, height: 35.h, fit: BoxFit.contain);
            },
          ),
        ),
        PreserveImageColors(
          child: Image.asset(AppAssets.visa, width: width.h, height: height.h),
        ),
        PreserveImageColors(
          child: Image.asset(AppAssets.mastercard, width: width.h, height: height.h),
        ),
        Platform.isIOS
            ? PreserveImageColors(
                child: Image.asset(AppAssets.applePay, width: width.h, height: height.h),
              )
            : SizedBox.shrink(),
      ],
    );
  }

  static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) {
    return Row(
      mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start,
      children: [
        appState.isArabic()
            ? Container()
            : Utils.buildSvgWithAssets(
                // icon: isSaudiCurrency ? AppAssets.saudi_riyal_icon : AppAssets.uae_dirham_symbol,
                icon: (getIt.get<AppState>().getAuthenticatedUser()!.outSa == 0) ? AppAssets.saudi_riyal_icon : AppAssets.uae_dirham_symbol,
                iconColor: iconColor,
                width: iconSize.h,
                height: iconSize.h,
                fit: BoxFit.contain,
              ),
        SizedBox(width: 5.h),
        paymentAmountWidget,
        SizedBox(width: 5.h),
        !appState.isArabic()
            ? Container()
            : Utils.buildSvgWithAssets(
                // icon: isSaudiCurrency ? AppAssets.saudi_riyal_icon : AppAssets.uae_dirham_symbol,
                icon: (getIt.get<AppState>().getAuthenticatedUser()!.outSa == 0) ? AppAssets.saudi_riyal_icon : AppAssets.uae_dirham_symbol,
                iconColor: iconColor,
                width: iconSize.h,
                height: iconSize.h,
                fit: BoxFit.contain,
              ),
      ],
    );
  }

  static Widget getPaymentAmountWithSymbol2(
    num habibWalletAmount, {
    double iconSize = 14,
    double? fontSize,
    double? letterSpacing,
    FontWeight? fontWeight,
    Color? iconColor,
    Color? textColor,
    bool isSaudiCurrency = true,
    bool isExpanded = true,
  }) {
    final resolvedIconColor = iconColor ?? AppColors.textColor;
    final resolvedTextColor = textColor ?? AppColors.blackColor;
    return RichText(
      maxLines: 1,
      text: TextSpan(
        children: [
          WidgetSpan(
            alignment: PlaceholderAlignment.baseline,
            baseline: TextBaseline.alphabetic,
            child: Utils.buildSvgWithAssets(icon: (getIt.get<AppState>().getAuthenticatedUser()!.outSa == 1) ? AppAssets.uae_dirham_symbol : AppAssets.saudi_riyal_icon, width: iconSize.h, height: iconSize.h, iconColor: resolvedIconColor),
          ),
          TextSpan(
            // text: NumberFormat.currency(locale: 'en_US', symbol: "  ", decimalDigits: 0).format(habibWalletAmount),
            // text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount)).toStringAsFixed(2)}",
            text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount))}",
            style: TextStyle(color: resolvedTextColor, fontSize: fontSize ?? 32.f, letterSpacing: letterSpacing ?? -4, fontFamily: "Poppins", fontWeight: fontWeight ?? FontWeight.w600, height: 1),
          ),
        ],
      ),
    );
  }

  static Future<bool> isGoogleServicesAvailable() async {
    GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
    String status = availability.toString().split('.').last;
    if (status == "success") {
      return true;
    }
    return false;
  }

  static Future<bool> checkConnection({bool bypassConnectionCheck = false}) async {
    if (bypassConnectionCheck) return true;
    List<ConnectivityResult> connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult.contains(ConnectivityResult.mobile) ||
        connectivityResult.contains(ConnectivityResult.wifi) ||
        connectivityResult.contains(ConnectivityResult.ethernet) ||
        connectivityResult.contains(ConnectivityResult.bluetooth) ||
        connectivityResult.contains(ConnectivityResult.vpn)) {
      return true;
    } else {
      return false;
    }
  }

  static String generateMd5Hash(String input) {
    return crypto.md5.convert(utf8.encode(input)).toString();
  }

  static String getAppointmentTransID(int projectID, int clinicID, int appoNo, {bool isAddMilliseconds = true}) {
    String currentMillis = DateTime.now().millisecondsSinceEpoch.toString();
    return '$projectID-$clinicID-$appoNo${isAddMilliseconds ? '-${currentMillis.substring(currentMillis.length - 5, currentMillis.length)}${currentMillis.substring(currentMillis.length - 5, currentMillis.length)}' : ""}';
  }

  static String getAdvancePaymentTransID(int projectID, int fileNumber) {
    return '$projectID-$fileNumber-${DateTime.now().millisecondsSinceEpoch}';
  }

  static Future<String> createFileFromString(String encodedStr, String ext) async {
    Uint8List bytes = base64.decode(encodedStr);
    String dir = (await getApplicationDocumentsDirectory()).path;
    File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext");
    await file.writeAsBytes(bytes);
    return file.path;
  }

  ///method to be used to get the text as per the langauge of the application
  static String getTextWRTCurrentLanguage(String? englishText, String? arabicText) {
    String? text = appState.isArabic() ? arabicText : englishText;
    return text ?? '';
  }

  static String formatNumberToInternationalFormat(num number, {String? currencySymbol, int decimalDigit = 0}) {
    return NumberFormat.currency(locale: 'en_US', symbol: currencySymbol ?? "", decimalDigits: decimalDigit).format(number);
  }

  static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) {
    if (hospital == null) return null;
    return PatientDoctorAppointmentList(
        filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC);
  }

  static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) {
    if (item == null) return null;
    return HospitalsModel(
      name: item.filterName,
      nameN: item.filterName,
      distanceInKilometers: item.distanceInKMs,
      isHMC: item.isHMC,
    );
  }

  static bool havePrivilege(int id) {
    bool isHavePrivilege = false;
    try {
      for (var element in appState.privilegeModelList) {
        if (element.id == id) isHavePrivilege = element.previlege!;
      }
    } catch (e) {
      print(e);
    }
    return isHavePrivilege;
  }

  static Future<void> openWebView({required String url}) async {
    try {
      Uri uri = Uri.parse(url);

      // Validate URL scheme for in-app browser
      if (!uri.hasScheme || (!uri.scheme.startsWith('http'))) {
        throw 'Invalid URL scheme. In-app browser only supports HTTP/HTTPS URLs';
      }

      // Check if URL can be launched
      if (await canLaunchUrl(uri)) {
        final launched = await launchUrl(
          uri,
          mode: LaunchMode.externalApplication,
        );

        if (!launched) {
          // Fallback to external browser
          await launchUrl(uri, mode: LaunchMode.externalApplication);
        }
      } else {
        // Fallback to external browser
        await launchUrl(uri, mode: LaunchMode.externalApplication);
      }
    } catch (e) {
      debugPrint('❌ Failed to open URL: $url - Error: $e');

      // Try external browser as last resort
      try {
        final uri = Uri.parse(url);
        await launchUrl(uri, mode: LaunchMode.externalApplication);
      } catch (e2) {
        debugPrint('❌ Failed to open URL in external browser: $e2');
        // Optionally show user-friendly error message
      }
    }
  }

  static Color getCardBorderColor(int currentQueueStatus) {
    switch (currentQueueStatus) {
      case 0:
        return AppColors.ratingColorYellow;
      case 1:
        return AppColors.primaryRedColor;
      case 2:
        return AppColors.successColor;
    }
    return AppColors.textColor;
  }

  static Color getCardButtonColor(int currentQueueStatus) {
    switch (currentQueueStatus) {
      case 0:
        return AppColors.textColor.withValues(alpha: 0.08);
      case 1:
        return AppColors.primaryRedColor;
      case 2:
        return AppColors.successColor;
    }
    return AppColors.primaryRedColor;
  }

  static Color getCardButtonTextColor(int currentQueueStatus) {
    switch (currentQueueStatus) {
      case 0:
        return Color(0xFFA2A2A2);
      case 1:
        return AppColors.whiteColor;
      case 2:
        return AppColors.whiteColor;
    }
    return AppColors.primaryRedColor;
  }

  static String getCardButtonText(int currentQueueStatus, String roomNumber) {
    switch (currentQueueStatus) {
      case 0:
        return LocaleKeys.pleaseWaitYouWillBeCalledForVitalSigns.tr();
      case 1:
        return LocaleKeys.pleaseVisitRoomForVitalSigns.tr(namedArgs: {'roomNumber': roomNumber.toString()});
      case 2:
        return LocaleKeys.pleaseVisitRoomToTheDoctor.tr(namedArgs: {'roomNumber': roomNumber.toString()});
    }
    return "";
  }

  static bool isDateToday(DateTime dateToCheck) {
    final DateTime now = DateTime.now();
    final DateTime today = DateTime(now.year, now.month, now.day);
    final DateTime checkDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);

    return checkDate == today;
  }

  static bool isClinicAllowedForRebook(int clinicID) {
    bool isAllowed = true;

    if (clinicID == 23 || clinicID == 47 || clinicID == 265 || clinicID == 253 || clinicID == 134) {
      isAllowed = false;
    }

    return isAllowed;
  }

  static bool containsArabic(String text) {
    // Using Unicode range for Arabic characters
    final RegExp arabicRegex = RegExp(r'[\u0600-\u06FF\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]');
    return arabicRegex.hasMatch(text);
  }

  static String toEnglishNumbers(String input) {
    const english = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
    const arabic = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
    const persian = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];

    String result = input;
    for (int i = 0; i < 10; i++) {
      result = result.replaceAll(arabic[i], english[i]);
      result = result.replaceAll(persian[i], english[i]);
    }
    return result;
  }

  /// Normalize Arabic text for search by handling Hamza variations and removing diacritics
  /// This makes searching more flexible for Arabic text
  static String normalizeArabicText(String text) {
    if (text.isEmpty) return text;

    String normalized = text;

    // Normalize Hamza variations to a single character 'ا'
    normalized = normalized.replaceAll('أ', 'ا'); // Hamza on Alif
    normalized = normalized.replaceAll('إ', 'ا'); // Hamza below Alif
    normalized = normalized.replaceAll('آ', 'ا'); // Madda on Alif
    normalized = normalized.replaceAll('ء', 'ا'); // Hamza alone
    normalized = normalized.replaceAll('ؤ', 'و'); // Hamza on Waw
    normalized = normalized.replaceAll('ئ', 'ي'); // Hamza on Ya

    // Normalize Alif Maqsura to Ya
    normalized = normalized.replaceAll('ى', 'ي');

    // Remove Arabic diacritics (Tashkeel)
    normalized = normalized.replaceAll(RegExp(r'[\u064B-\u065F]'), ''); // Fatha, Damma, Kasra, etc.
    normalized = normalized.replaceAll(RegExp(r'[\u0670]'), ''); // Superscript Alif
    normalized = normalized.replaceAll(RegExp(r'[\u0640]'), ''); // Tatweel

    return normalized;
  }
}
