import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/models/generic_response_model.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/models/kiosk_language_config_model.dart';
import 'package:hmg_qline/models/kiosk_queue_model.dart';
import 'package:hmg_qline/models/kiosk_ticket_model.dart';
import 'package:hmg_qline/models/prayers_widget_model.dart';
import 'package:hmg_qline/models/rss_feed_model.dart';
import 'package:hmg_qline/models/weathers_widget_model.dart';
import 'package:hmg_qline/repositories/screen_details_repo.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/views/view_helpers/info_components.dart';
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
// import 'package:timezone/browser.dart' as tz;

class ScreenConfigViewModel extends ChangeNotifier {
  final ScreenDetailsRepo screenDetailsRepo;
  final CacheService cacheService;
  final ConnectivityService connectivityService;

  ScreenConfigViewModel({
    required this.screenDetailsRepo,
    required this.cacheService,
    required this.connectivityService,
  });

  Future<void> initializeScreenConfigVM() async {
    await getGlobalConfigurationsByIP();
    await getLastTimeUpdatedFromCache();
    listenNetworkConnectivity();
    getTheWidgetsConfigurationsEveryMidnight();
  }

  // Future<void> initializeTimezone() async {
  //   await tz.initializeTimeZone();
  //   var detroit = tz.getLocation('America/Detroit');
  //   var now = tz.TZDateTime.now(detroit);
  // }

  Future<void> waitForIPAndInitializeConfigVM() async {
    while (currentScreenIP == "") {
      await getCurrentScreenIP();
      if (currentScreenIP != "") {
        initializeScreenConfigVM();
      } else {
        await Future.delayed(const Duration(seconds: 2));
      }
    }
  }

  bool isInternetConnected = true;

  updateIsInternetConnected(bool value) {
    isInternetConnected = value;
    notifyListeners();
  }

  bool isHubConnected = true;

  updateIsHubConnected(bool value) {
    isHubConnected = value;
    notifyListeners();
  }

  ViewState state = ViewState.idle;

  void updateViewState(ViewState viewState) {
    state = viewState;
    notifyListeners();
  }

  updateCurrentScreenRotation(ScreenOrientationEnum value) {
    globalConfigurationsModel.orientationTypeEnum = value;
    notifyListeners();
  }

  ScreenTypeEnum currentScreenTypeEnum = ScreenTypeEnum.waitingAreaScreen;

  updateCurrentScreenTypeEnum(ScreenTypeEnum value) {
    currentScreenTypeEnum = value;
  }

  QTypeEnum currentQTypeEnum = QTypeEnum.lab;

  updateCurrentQTypeEnum(QTypeEnum value) {
    currentQTypeEnum = value;
  }

  String currentScreenIP = "";

  Future<void> getCurrentScreenIP() async {
    if (useTestIP) {
      currentScreenIP = AppConstants.testIP;
    } else {
      currentScreenIP = await connectivityService.getCurrentScreenIP();
      log("currentScreenIP: $currentScreenIP");
    }
  }

  void listenNetworkConnectivity() {
    return connectivityService.subscribeToConnectivityChange(onInternetDisConnected: () {
      updateIsInternetConnected(false);
      updateIsHubConnected(false);
    }, onInternetConnected: () {
      updateIsInternetConnected(true);
      QueuingViewModel queuingViewModel = getIt.get<QueuingViewModel>();
      queuingViewModel.startHubConnection();
    });
  }

  GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel();

  Future<void> getGlobalConfigurationsByIP() async {
    GlobalConfigurationsModel? response = await screenDetailsRepo.getGlobalScreenConfigurations(ipAddress: currentScreenIP);

    if (response == null) {
      logger.e("response was: $response");
      return;
    }
    updateGlobalConfigurationsModel(value: response);
    updateCurrentScreenTypeEnum(globalConfigurationsModel.screenTypeEnum);
    updateCurrentQTypeEnum(globalConfigurationsModel.qTypeEnum);

    notifyListeners();
  }

  void updateGlobalConfigurationsModel({required var value, bool needNotify = false, bool shouldUpdateNextPrayer = false}) {
    globalConfigurationsModel = value;
    log(" Updating@2 : ${globalConfigurationsModel.isToneReq}");

    if (needNotify) {
      notifyListeners();
    }
    if (shouldUpdateNextPrayer) {
      getNextPrayerToShow();
    }

    getInfoWidgetsDetailsFromServer();
  }

  Future<void> getInfoWidgetsDetailsFromServer() async {
    if (globalConfigurationsModel.isWeatherReq) {
      await getWeatherDetailsFromServer();
    }
    if (globalConfigurationsModel.isPrayerTimeReq) {
      await getPrayerDetailsFromServer();
    }
    if (globalConfigurationsModel.isRssFeedReq) {
      await getRssFeedDetailsFromServer();
    }

    int currentDate = DateTime.now().millisecondsSinceEpoch;
    await cacheService.setLastTimeUpdatedInCache(lasTimeUpdated: currentDate.toString());
  }

  RssFeedModel rssFeedModel = RssFeedModel();

  Future<void> getRssFeedDetailsFromServer() async {
    RssFeedModel? response = await screenDetailsRepo.getRssFeedDetailsByLanguageID(languageId: 0);

    if (response == null) {
      return;
    }
    rssFeedModel = response;
    notifyListeners();
  }

  PrayersWidgetModel prayersWidgetModel = PrayersWidgetModel();

  Future<void> getPrayerDetailsFromServer() async {
    double testLatitude = 24.722136;
    double testLongitude = 46.774303;
    PrayersWidgetModel? response = await screenDetailsRepo.getPrayerDetailsByLatLong(
      latitude: globalConfigurationsModel.projectLatitude == 0.0 ? testLatitude : globalConfigurationsModel.projectLatitude ?? testLatitude,
      longitude: globalConfigurationsModel.projectLongitude == 0.0 ? testLongitude : globalConfigurationsModel.projectLongitude ?? testLongitude,
    );

    if (response == null) {
      return;
    }

    prayersWidgetModel = response;
    getNextPrayerToShow();
    notifyListeners();
  }

  WeathersWidgetModel weathersWidgetModel = WeathersWidgetModel();

  Future<void> getWeatherDetailsFromServer() async {
    int testCityKey = 297030;
    WeathersWidgetModel? response = await screenDetailsRepo.getWeatherDetailsByCity(
      cityId: ((globalConfigurationsModel.cityKey == null || globalConfigurationsModel.cityKey == 0) ? testCityKey : globalConfigurationsModel.cityKey).toString(),
    );

    if (response == null) {
      return;
    }
    weathersWidgetModel = response;
    notifyListeners();
  }

  String nextPrayerToShowWithTime = '';

  void getNextPrayerToShow() async {
    final current = DateTime.now();
    log("Checking Namaz time Locally at ${current.toString()} and ${current.timeZoneName} ");

    if (globalConfigurationsModel.isPrayerTimeReq && prayersWidgetModel.fajr == null) {
      await getPrayerDetailsFromServer();
    }

    // log("prayersWidgetModel.dhuhr: ${prayersWidgetModel.dhuhr!}");
    // log("dhuhr: ${prayersWidgetModel.dhuhr!.toDateTimeFromInt()}");
    // log("current: $current");

    if (prayersWidgetModel.fajr != null && prayersWidgetModel.fajr!.toDateTimeFromInt().isAfter(current)) {
      final namazTime = prayersWidgetModel.fajr!.toFormattedDateTimeFromInt();
      nextPrayerToShowWithTime = "${globalConfigurationsModel.fajarText} at $namazTime";
      notifyListeners();
      return;
    }
    if (prayersWidgetModel.dhuhr != null && prayersWidgetModel.dhuhr!.toDateTimeFromInt().isAfter(current)) {
      final namazTime = prayersWidgetModel.dhuhr!.toFormattedDateTimeFromInt();
      nextPrayerToShowWithTime = "${globalConfigurationsModel.dhuhrText} at $namazTime";
      notifyListeners();
      return;
    }
    if (prayersWidgetModel.asr != null && prayersWidgetModel.asr!.toDateTimeFromInt().isAfter(current)) {
      final namazTime = prayersWidgetModel.asr!.toFormattedDateTimeFromInt();
      nextPrayerToShowWithTime = "${globalConfigurationsModel.asarText} at $namazTime";
      notifyListeners();
      return;
    }
    if (prayersWidgetModel.maghrib != null && prayersWidgetModel.maghrib!.toDateTimeFromInt().isAfter(current)) {
      final namazTime = prayersWidgetModel.maghrib!.toFormattedDateTimeFromInt();
      nextPrayerToShowWithTime = "${globalConfigurationsModel.maghribText} at $namazTime";
      notifyListeners();
      return;
    }
    if (prayersWidgetModel.isha != null && prayersWidgetModel.isha!.toDateTimeFromInt().isAfter(current)) {
      final namazTime = prayersWidgetModel.isha!.toFormattedDateTimeFromInt();
      nextPrayerToShowWithTime = "${globalConfigurationsModel.ishaText} at $namazTime";
      notifyListeners();
      return;
    }
  }

  int counter = 0;

  Future<void> getTheWidgetsConfigurationsEveryMidnight() async {
    if (!(globalConfigurationsModel.isWeatherReq) && !(globalConfigurationsModel.isPrayerTimeReq) && !(globalConfigurationsModel.isRssFeedReq)) {
      return;
    }

    DateTime current = DateTime.now();
    Stream timer = Stream.periodic(const Duration(minutes: 1), (i) {
      current = current.add(const Duration(minutes: 1));
      return current;
    });

    timer.listen((data) async {
      DateTime dateTime = DateTime.parse(data.toString());

      counter++;

      log("counterValue: $counter");

      if (counter == 60 && globalConfigurationsModel.isRssFeedReq) {
        await getRssFeedDetailsFromServer();
      }

      if (globalConfigurationsModel.isWeatherReq) {
        if (dateTime.day > currentLastTimeUpdated.day) {
          await getWeatherDetailsFromServer();
        }
      }

      if (globalConfigurationsModel.isPrayerTimeReq) {
        if (dateTime.day > currentLastTimeUpdated.day) {
          await getPrayerDetailsFromServer();
        }
      }

      if (globalConfigurationsModel.isRssFeedReq) {
        if (dateTime.day > currentLastTimeUpdated.day) {
          await getRssFeedDetailsFromServer();
        }
      }

      getNextPrayerToShow();
    });
  }

  DateTime currentLastTimeUpdated = DateTime.now();

  Future<void> getLastTimeUpdatedFromCache() async {
    DateTime? response = await cacheService.getLastTimeUpdatedFromCache();
    if (response != null) {
      currentLastTimeUpdated = response;
    }
  }

//   *************************** KIOSK FUNCTIONS *************************

  KioskScreenStateEnums kioskScreenStateEnum = KioskScreenStateEnums.languageState;

  void updateKioskScreenState(KioskScreenStateEnums state) {
    kioskScreenStateEnum = state;
    notifyListeners();
  }

  KioskPatientTicket? kioskPatientTicket = KioskPatientTicket();

  void updateTicketGeneratedFromKiosk(KioskPatientTicket? value) {
    kioskPatientTicket = value;
    notifyListeners();
  }

  KioskLanguageConfigModel? currentSelectedKioskLanguage;

  void updateCurrentSelectedKioskLanguageModel(KioskLanguageConfigModel value) {
    currentSelectedKioskLanguage = value;
    notifyListeners();
  }

  late KioskQueueModel currentSelectedKioskQueueModel;

  void updateCurrentSelectedKioskQueueModel(KioskQueueModel value) {
    currentSelectedKioskQueueModel = value;
    notifyListeners();
  }

  Future<bool> generateTicketForQueue({required KioskQueueModel kioskQueueModel, int patientId = 0}) async {
    updateKioskScreenState(KioskScreenStateEnums.busyState);
    KioskPatientTicket? kioskPatientTicket = await createTicketFromKiosk(
      projectId: kioskQueueModel.projectID ?? 0,
      queueId: kioskQueueModel.queueID ?? 0,
      patientId: patientId,
    );
    if (kioskPatientTicket == null) {
      updateKioskScreenState(KioskScreenStateEnums.languageState);
      return false;
    }
    updateTicketGeneratedFromKiosk(kioskPatientTicket);
    updateKioskScreenState(KioskScreenStateEnums.ticketNumState);
    return true;
  }

  Future<void> createTestTickets({required int numOfTicketsToCreate}) async {
    int startTicket = 123457100;

    for (int i = 0; i < numOfTicketsToCreate; i++) {
      GenericRespModel? response = await screenDetailsRepo.createTestTickets(ticketNumber: startTicket);
      startTicket = startTicket + 1;
      if (response == null || response.messageStatus != 1) {
        log("response null from createNextTickets");
        return;
      }
    }

    log("last ticket is: $startTicket ");
  }

  Future<KioskPatientTicket?> createTicketFromKiosk({required int projectId, required int queueId, int patientId = 0}) async {
    try {
      GenericRespModel? response = await screenDetailsRepo.createTicketFromKiosk(
        projectId: projectId,
        queueId: queueId,
        patientId: patientId,
      );
      if (response == null || response.messageStatus != 1) {
        logger.e("response null from createTicketFromKiosk");
        return null;
      }
      return response.data;
    } catch (e) {
      InfoComponents.showToast(e.toString());
      logger.i(e.toString());
      return null;
    }
  }

  Future<void> acknowledgeTicket({required int ticketQueueID}) async {
    GenericRespModel? response = await screenDetailsRepo.acknowledgeTicket(
      ipAddress: currentScreenIP,
      ticketQueueID: ticketQueueID,
      qTypeEnum: globalConfigurationsModel.qTypeEnum,
    );
    if (response == null || response.messageStatus != 1) {
      logger.e("response null from acknowledgeTicket");
      return;
    } else {
      logger.i("response from acknowledgeTicket ${response.data}");
    }
  }

  Future<void> acknowledgeTicketForAppointmentOnly({required int ticketQueueID, required String ipAddress, required CallTypeEnum callTypeEnum}) async {
    GenericRespModel? response = await screenDetailsRepo.acknowledgeTicketForAppointment(
      ticketId: ticketQueueID,
      ipAddress: ipAddress,
      callTypeEnum: callTypeEnum,
    );
    if (response == null || response.messageStatus != 1) {
      logger.e("response null from acknowledgeTicket");
      return;
    } else {
      logger.i("response from acknowledgeTicket ${response.data}");
    }
  }

// ***************************** TEXT INPUT PATIENT ID  FUNCTIONS *********************

  final TextEditingController patientIdController = TextEditingController();

  @override
  void dispose() {
    patientIdController.dispose();
    super.dispose();
  }

  Future<void> onPatientIdSubmitted(String text) async {
    int? patientId = int.tryParse(text);
    if (patientId != null && patientId > 0) {
      isGeneratingTicket = true;
      await generateTicketForQueue(kioskQueueModel: currentSelectedKioskQueueModel, patientId: patientId);
      patientIdController.clear();
      await Future.delayed(const Duration(seconds: 2)).whenComplete(() => isGeneratingTicket = false);
    }
  }

//   *************************** QR SCANNER FUNCTIONS *************************

  Barcode? qrCodeResult;
  QRViewController? qrViewController;
  bool isGeneratingTicket = false;

  Future<void> flipCamera() async {
    await qrViewController!.flipCamera();
  }

  void onQRViewCreated({
    required QRViewController controller,
    required VoidCallback onSuccess,
    required VoidCallback onFailure,
  }) async {
    qrViewController = controller;

    controller.scannedDataStream.listen((scanData) async {
      logger.i("Found: ${scanData.code} and isGeneratingTicket: $isGeneratingTicket");
      if (isGeneratingTicket) return;
      await validateAndGenerateTicketFromQR(data: scanData, onFailure: onFailure, onSuccess: onSuccess);
    });
    await flipCamera();
  }

  Future<void> validateAndGenerateTicketFromQR({required var data, required VoidCallback onSuccess, required VoidCallback onFailure}) async {
    if (data == null) return;
    qrCodeResult = data;
    notifyListeners();

    final code = qrCodeResult!.code;
    int? patientId = int.tryParse(code ?? '');
    if (patientId != null && patientId > 0) {
      isGeneratingTicket = true;
      bool status = await generateTicketForQueue(kioskQueueModel: currentSelectedKioskQueueModel, patientId: patientId);
      qrCodeResult = null;
      notifyListeners();

      if (status) {
        log("status: $status");
        onSuccess();
      }

      await Future.delayed(const Duration(seconds: 2)).whenComplete(() => isGeneratingTicket = false);
    }
  }

  Future<void> reassemble() async {
    if (qrViewController != null) {
      if (defaultTargetPlatform == TargetPlatform.android) {
        await qrViewController!.pauseCamera();
      } else if (defaultTargetPlatform == TargetPlatform.iOS) {
        await qrViewController!.resumeCamera();
      }
      await qrViewController!.flipCamera();
    }
  }
}
