import 'dart:async';
import 'dart:developer';
import 'package:flutter/cupertino.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/models/ticket_model.dart';
import 'package:hmg_qline/repositories/screen_details_repo.dart';
import 'package:hmg_qline/repositories/signalR_repo.dart';
import 'package:hmg_qline/services/audio_service.dart';
import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/text_to_speech_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/view_models/screen_config_view_model.dart';

class QueuingViewModel extends ChangeNotifier {
  final ScreenDetailsRepo screenDetailsRepo;
  final SignalrRepo signalrRepo;
  final CacheService cacheService;
  final AudioService audioService;
  final TextToSpeechService textToSpeechService;

  QueuingViewModel({
    required this.screenDetailsRepo,
    required this.signalrRepo,
    required this.cacheService,
    required this.audioService,
    required this.textToSpeechService,
  });

  Future<void> initializeQueueingVM() async {
    await startHubConnection();
    initializeAudioPlayer();
    initializeTextToSpeech();
  }

  Future<void> startHubConnection() async {
    ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
    await signalrRepo.startHubConnection(
      deviceIp: screenConfigViewModel.currentScreenIP,
      onHubTicketCall: onHubTicketCall,
      onHubConfigCall: onHubConfigCall,
      onHubReconnected: onHubReconnected,
      onHubDisconnected: onHubDisconnected,
      onHubConnecting: onHubConnecting,
    );
  }

  initializeAudioPlayer() {
    audioService.listenAudioPlayerEvents(onToneCompleted: onToneCompleted);
  }

  initializeTextToSpeech() {
    textToSpeechService.listenToTextToSpeechEvents(onVoiceCompleted: onVoiceCompleted);
  }

  Future<void> onHubConfigCall(var response) async {
    if (response != null && response.isNotEmpty) {
      var data = response.first['data'];
      GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel.fromJson(
        json: data,
        qType: response.first['qType'] ?? 1,
        screenType: response.first['screenType'] ?? 1,
      );

      log("onHubConfigCall: ${globalConfigurationsModel.toString()}");

      ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
      screenConfigViewModel.updateGlobalConfigurationsModel(value: globalConfigurationsModel, needNotify: true, shouldUpdateNextPrayer: true);
    }
  }

  Future<void> onHubReconnected(var response) async {
    log("onHubConnected: $response");
    ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
    screenConfigViewModel.updateIsHubConnected(true);
    screenConfigViewModel.notifyListeners();
  }

  Future<void> onHubDisconnected(var response) async {
    log("onHubDisconnected: $response");
    ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
    screenConfigViewModel.updateIsHubConnected(false);
    screenConfigViewModel.notifyListeners();
  }

  Future<void> onHubConnecting(var response) async => log("onHubConnecting: $response");

  Future<void> onToneCompleted() async {
    GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
    if (true) {
      await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq));
    } else {
      waitAndCallNextTicketIfAvailable();
    }
  }

  Future<void> onVoiceCompleted() async {
    log("isSpeechCompleted::: $isSpeechCompleted");
    if (isSpeechCompleted) {
      waitAndCallNextTicketIfAvailable();
    }
  }

  waitAndCallNextTicketIfAvailable() {
    GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
    Timer(Duration(seconds: globalConfigurationsModel.concurrentCallDelaySec), () async {
      if (queueTickets.isNotEmpty) {
        int index = itemAlreadyAvailableAtIndex(ticketToSearch: queueTickets.first, listToSearchIn: currentTickets);
        if (index != -1) {
          currentTickets.removeAt(index);
        }
        currentTickets.insert(0, queueTickets.first);

        if (currentTickets.length > globalConfigurationsModel.screenMaxDisplayPatients) {
          currentTickets.removeLast();
        }
        notifyListeners();
        queueTickets.removeAt(0);
        callTicketOnScreen(ticketData: currentTickets.first.ticketModel);
      } else {
        isCallingInProgress = false;
      }
    });
  }

  Future<void> onHubTicketCall(List<Object?>? response) async {
    logger.i("onHubTicketCall: $response");
    log("isCallingInProgress: $isCallingInProgress");
    if (response != null && response.isNotEmpty) {
      TicketDetailsModel ticketDetailsModel = TicketDetailsModel.fromJson(response.first as Map<String, dynamic>);
      addNewTicket(ticketDetailsModel);
    }
  }

  int itemAlreadyAvailableAtIndex({required TicketDetailsModel ticketToSearch, required List<TicketDetailsModel> listToSearchIn}) {
    int isFoundAt = -1;
    try {
      isFoundAt = listToSearchIn.indexWhere((ticket) {
        log("ticket.ticketModel!.queueNo: ${ticket.ticketModel!.queueNo}");
        log("ticketToSearch.ticketModel!.queueNo: ${ticketToSearch.ticketModel!.queueNo}");
        return ticket.ticketModel!.queueNo == ticketToSearch.ticketModel!.queueNo;
      });
      log("itemAlreadyAvailableAtIndex: $isFoundAt");
      return isFoundAt;
    } catch (e) {
      log("Error isItemAlreadyAvailableInList: ${e.toString()}");
      return isFoundAt;
    }
  }

  bool isCallingInProgress = false;

  // Tickets Management
  List<TicketDetailsModel> currentTickets = [];
  List<TicketDetailsModel> queueTickets = [];

  void addNewTicket(TicketDetailsModel ticket) {
    if (!isCallingInProgress) {
      int index = itemAlreadyAvailableAtIndex(ticketToSearch: ticket, listToSearchIn: currentTickets);
      if (index != -1) {
        currentTickets.removeAt(index);
      }
      currentTickets.insert(0, ticket);

      GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
      if (currentTickets.length > globalConfigurationsModel.screenMaxDisplayPatients) {
        currentTickets.removeLast();
      }
      callTicketOnScreen(ticketData: currentTickets.first.ticketModel);
    } else {
      int index = itemAlreadyAvailableAtIndex(ticketToSearch: ticket, listToSearchIn: queueTickets);
      if (index != -1) {
        queueTickets.removeAt(index);
      }
      queueTickets.add(ticket);
      log("inQueue Length: ${queueTickets.length}");
    }
    notifyListeners();
  }

  Future<void> testSpeech() async {
    textToSpeechService.speechTextTest("Ticket Number ... ABC One Tow Three.. Please visit Doctor.");
  }

  Future<void> callTicketOnScreen({required TicketData? ticketData}) async {
    if (ticketData == null) return;
    ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
    GlobalConfigurationsModel globalConfigurationsModel = screenConfigViewModel.globalConfigurationsModel;
    if (globalConfigurationsModel.qTypeEnum == QTypeEnum.appointment) {
      screenConfigViewModel.acknowledgeTicketForAppointmentOnly(
        ticketQueueID: ticketData.id ?? 0,
        ipAddress: screenConfigViewModel.currentScreenIP,
        callTypeEnum: ticketData.callTypeEnum,
      );
    } else {
      screenConfigViewModel.acknowledgeTicket(ticketQueueID: ticketData.id ?? 0);
    }

    log("globalConfigurationsModel: ${globalConfigurationsModel.toString()}");
    if (true) {
      isCallingInProgress = true;
      await audioService.playTone(path: AppAssets.callTone, isMute: !(globalConfigurationsModel.isToneReq));
    } else if (globalConfigurationsModel.isVoiceReq) {
      isCallingInProgress = true;
      await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq));
    } else {
      waitAndCallNextTicketIfAvailable();
    }
  }
}

//  Future<void> callTicketOnScreen({required TicketData? ticketData}) async {
//     if (ticketData == null) return;
//     ScreenConfigViewModel screenConfigViewModel = getIt.get<ScreenConfigViewModel>();
//     GlobalConfigurationsModel globalConfigurationsModel = screenConfigViewModel.globalConfigurationsModel;
//     if (globalConfigurationsModel.qTypeEnum == QTypeEnum.appointment) {
//       screenConfigViewModel.acknowledgeTicketForAppointmentOnly(
//         ticketQueueID: ticketData.id ?? 0,
//         ipAddress: screenConfigViewModel.currentScreenIP,
//         callTypeEnum: ticketData.callTypeEnum,
//       );
//     } else {
//       screenConfigViewModel.acknowledgeTicket(ticketQueueID: ticketData.id ?? 0);
//     }
//     if (globalConfigurationsModel.isToneReq) {
//       isCallingInProgress = true;
//       await audioService.playTone(path: AppAssets.callTone, isMute: !(globalConfigurationsModel.isToneReq));
//     } else if (globalConfigurationsModel.isVoiceReq) {
//       isCallingInProgress = true;
//       await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq));
//     } else {
//       waitAndCallNextTicketIfAvailable();
//     }
//   }
