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/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(onAudioCompleted: onToneCompleted);
  }

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

  Future<void> onHubConfigCall(var response) async {
    log("onHubConfigCall: $response");
    if (response != null && response.isNotEmpty) {
      var data = response.first['data'];
      GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel.fromJson(data as Map<String, dynamic>);
      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);
    log("screenConfigViewModel: ${screenConfigViewModel.isHubConnected}");
    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 (globalConfigurationsModel.isVoiceReq) {
      textToSpeechService.speechText(
        globalConfigurationsModel: globalConfigurationsModel,
        ticket: currentTickets.first,
      );
    } else {
      waitAndCallNextTicketIfThere();
    }
  }

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

  waitAndCallNextTicketIfThere() {
    GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
    Timer(Duration(seconds: globalConfigurationsModel.concurrentCallDelaySec), () async {
      if (queueTickets.isNotEmpty) {
        currentTickets.insert(0, queueTickets.first);
        if (currentTickets.length > globalConfigurationsModel.screenMaxDisplayPatients) {
          currentTickets.removeLast();
        }
        notifyListeners();
        queueTickets.removeAt(0);
        voiceCallTicket(ticketData: currentTickets.first.ticketModel);
      } else {
        isCallingInProgress = false;
      }
    });
  }

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

  bool isCallingInProgress = false;

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

  void addNewTicket(TicketDetailsModel ticket) {
    if (!isCallingInProgress) {
      currentTickets.insert(0, ticket);
      GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
      if (currentTickets.length > globalConfigurationsModel.screenMaxDisplayPatients) {
        currentTickets.removeLast();
      }
      voiceCallTicket(ticketData: currentTickets.first.ticketModel);
    } else {
      queueTickets.add(ticket);
      log("inQueue Length: ${queueTickets.length}");
    }
    notifyListeners();
  }

  Future<void> testSpeech() async {
    textToSpeechService.speechTextTest("يرجى  التوجه .. إلى العداد  ..رقم .. 12");
  }

  Future<void> voiceCallTicket({required TicketData? ticketData}) async {
    if (ticketData == null) return;
    GlobalConfigurationsModel globalConfigurationsModel = getIt.get<ScreenConfigViewModel>().globalConfigurationsModel;
    if (globalConfigurationsModel.isToneReq) {
      isCallingInProgress = true;
      await audioService.playTone(path: AppAssets.callTone);
    } else if (globalConfigurationsModel.isVoiceReq) {
      isCallingInProgress = true;
      await textToSpeechService.speechText(
        globalConfigurationsModel: globalConfigurationsModel,
        ticket: currentTickets.first,
      );
    } else {
      waitAndCallNextTicketIfThere();
    }
  }
}
