import 'dart:async';

import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/marathon/demo_marathon_repo.dart';
import 'package:mohem_flutter_app/api/marathon/marathon_api_client.dart';
import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/consts.dart';
import 'package:mohem_flutter_app/classes/utils.dart';
import 'package:mohem_flutter_app/config/routes.dart';
import 'package:mohem_flutter_app/models/marathon/marathon_model.dart';
import 'package:mohem_flutter_app/models/marathon/question_model.dart';
import 'package:mohem_flutter_app/models/marathon/winner_model.dart';
import 'package:mohem_flutter_app/models/privilege_list_model.dart';
import 'package:mohem_flutter_app/ui/marathon/widgets/question_card.dart';
import 'package:video_player/video_player.dart';

class MarathonProvider extends ChangeNotifier {
  //************************************************ VARIABLES **********************************************************

  final AppinioSwiperController swiperController = AppinioSwiperController();

  MarathonDetailModel marathonDetailModel = MarathonDetailModel();
  MarathonDetailModel demoMarathonDetailModel = MarathonDetailModel();
  List<CardContent> cardContentList = <CardContent>[];
  QuestionModel currentQuestion = QuestionModel();
  List<QuestionCardStatus> answerStatusesList = <QuestionCardStatus>[];
  QuestionCardStatus questionCardStatus = QuestionCardStatus.question;
  List<WinnerModel>? selectedWinners;
  int? selectedOptionIndex;
  String? selectedOptionId;
  int? totalQualifiers;

  String? gapTimeImage;
  String? gapTimeText;
  int? gapTimeType;

  bool iAmWinner = false;
  bool isGettingQualifiers = false;
  bool isPrivilegedWithMarathon = false;

  bool _isLoading = false;

  bool get isLoading => _isLoading;

  set isLoading(bool value) {
    _isLoading = value;
    notifyListeners();
  }

  bool _isUpComingMarathon = true;

  bool get isUpComingMarathon => _isUpComingMarathon;

  set isUpComingMarathon(bool value) {
    _isUpComingMarathon = value;

    notifyListeners();
  }

  bool _itsMarathonTime = false;

  bool get itsMarathonTime => _itsMarathonTime;

  set itsMarathonTime(bool value) {
    _itsMarathonTime = value;
    notifyListeners();
  }

  bool _canPlayDemo = true;

  bool get canPlayDemo => _canPlayDemo;

  set canPlayDemo(bool value) {
    _canPlayDemo = value;
    notifyListeners();
  }

  bool _isButtonEnabled = false;

  bool get isButtonEnabled => _isButtonEnabled;

  set isButtonEnabled(bool value) {
    _isButtonEnabled = value;
    notifyListeners();
  }

  bool _isUserWaiting = false;

  bool get isUserWaiting => _isUserWaiting;

  set isUserWaiting(bool value) {
    _isUserWaiting = value;
    notifyListeners();
  }

  bool _isMarathonCompleted = false;

  bool get isMarathonCompleted => _isMarathonCompleted;

  set isMarathonCompleted(bool value) {
    _isMarathonCompleted = value;
    notifyListeners();
  }

  bool isUserOutOfGame = false;

  set updateIsUserOutOfGame(bool value) {
    isUserOutOfGame = value;
    notifyListeners();
  }

  int _currentQuestionNumber = 0;

  int get currentQuestionNumber => _currentQuestionNumber;

  set currentQuestionNumber(int value) {
    _currentQuestionNumber = value;
    notifyListeners();
  }

  int _totalMarathoners = 0;

  int get totalMarathoners => _totalMarathoners;

  set totalMarathoners(int value) {
    _totalMarathoners = value;
    notifyListeners();
  }

  //****************SPONSOR VIDEO PLAYER**********

  late VideoPlayerController videoController;

  Future<void> initializeVideoPlayer() async {
    videoController = VideoPlayerController.network(ApiConsts.marathonBaseUrlServices + marathonDetailModel.sponsors!.first.video!);
    await videoController.initialize();
    await videoController.play();
    await videoController.setVolume(1.0);
    await videoController.setLooping(false);
    totalSponsorVideoSeconds = videoController.value.duration.inSeconds;
    notifyListeners();
  }

  void disposeVideoPlayer() {
    videoController.dispose();
    notifyListeners();
  }

  // 9c47d281-c5b5-4b5d-a90a-08dacb8cbdb6
  // MarathonI
  //************************************************ TIMERS **********************************************************

  int sponsorsSecondsCounter = 0;
  int totalSponsorVideoSeconds = 0;
  Timer timerForSponsorVideo = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});

  void startTimerForSponsorVideo() {
    const Duration oneSec = Duration(seconds: 1);
    timerForSponsorVideo = Timer.periodic(
      oneSec,
      (Timer timer) async {
        sponsorsSecondsCounter++;
        if (totalSponsorVideoSeconds == 0) {
          timer.cancel();
          notifyListeners();
          return;
        } else {
          totalSponsorVideoSeconds--;
        }

        notifyListeners();
      },
    );
  }

  int totalSecondsToWaitForMarathon = 30;

  set updateTotalSecondsToWaitForMarathon(int value) {
    totalSecondsToWaitForMarathon = value;
    notifyListeners();
  }

  Timer timerToWaitForMarathon = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});

  void startTimerToWaitForMarathon() {
    const Duration oneSec = Duration(seconds: 1);
    timerToWaitForMarathon = Timer.periodic(
      oneSec,
      (Timer timer) async {
        if (totalSecondsToWaitForMarathon == 0) {
          if (isUserWaiting) {
            MarathonApiClient().joinMarathonAsParticipant().whenComplete(() async {
              await callNextQuestionApi();
            });
          } else {
            isButtonEnabled = false;
          }
          timer.cancel();
          return;
        } else {
          totalSecondsToWaitForMarathon--;
        }
        notifyListeners();
      },
    );
  }

  int totalCurrentQuestionTime = 0;
  int currentGapTime = 0;
  Timer timerForQuestion = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});

  void startTimerForQuestion() {
    const Duration oneSec = Duration(seconds: 1);
    timerForQuestion = Timer.periodic(
      oneSec,
      (Timer timer) async {
        // This 2 is just to show the color of answer tile for 1 and then update card status
        if (totalCurrentQuestionTime - currentGapTime == 1) {
          getCorrectAnswerAndUpdateAnswerColor();
        }

        if (totalCurrentQuestionTime - currentGapTime == -2) {
          updateCardStatusToAnswer();

          // scheduleMicrotask(() async {
          //   if (AppState().getIsDemoMarathon || isUserOutOfGame) {
          //     await callNextQuestionApi();
          //   } else {
          //     await callSubmitOptionApi().then((bool value) async {
          //       updateIsUserOutOfGame = !value;
          //       await callNextQuestionApi();
          //     });
          //   }
          // });

          if (currentQuestionNumber == (AppState().getIsDemoMarathon ? demoMarathonDetailModel.totalQuestions! : marathonDetailModel.totalQuestions!)) {
            isGettingQualifiers = true;
            updateQuestionCardStatus(QuestionCardStatus.findingWinner);
            timer.cancel();
            cancelTimer();
            notifyListeners();
          }
          totalCurrentQuestionTime--;
        }

        if (totalCurrentQuestionTime == 0) {
          gapTimeImage = currentQuestion.gapImage;
          gapTimeText = currentQuestion.gapText;
          gapTimeType = currentQuestion.gapType;
          updateCardData();

          return;
        } else {
          if (totalCurrentQuestionTime - currentGapTime != -2) {
            totalCurrentQuestionTime--;
          }
        }

        notifyListeners();
      },
    );
  }

  int totalSecondsToWaitForWinner = 30;
  Timer timerForWinnerSelection = Timer.periodic(const Duration(seconds: 1), (Timer timer) {});

  void startTimerForWinnerSelection() {
    const Duration oneSec = Duration(seconds: 1);
    timerForWinnerSelection = Timer.periodic(
      oneSec,
      (Timer timer) async {
        if (totalSecondsToWaitForWinner == 1) {
          await callGetSelectedWinnersApi().whenComplete(() => updateQuestionCardStatus(QuestionCardStatus.winnerFound));
          timer.cancel();
          return;
        } else if (totalSecondsToWaitForWinner == 15) {
          totalSecondsToWaitForWinner--;
          await callGetQualifiersApi();
        } else {
          totalSecondsToWaitForWinner--;
        }

        notifyListeners();
      },
    );
  }

  //************************************************ FUNCTIONS **********************************************************

  Future<bool> callSubmitOptionApi() async {
    return await MarathonApiClient().submitSelectedOption(marathonId: marathonDetailModel.id!, questionId: currentQuestion.id, selectedAnswerId: selectedOptionId);
  }

  Future<void> callGetQualifiersApi() async {
    if (AppState().getIsDemoMarathon) {
      totalQualifiers = isUserOutOfGame ? 0 : 1;
    } else {
      totalQualifiers = await MarathonApiClient().getQualifiers(marathonId: marathonDetailModel.id!);
    }
    isGettingQualifiers = false;
    notifyListeners();
  }

  Future<void> callGetSelectedWinnersApi() async {
    if (AppState().getIsDemoMarathon) {
      return;
    }
    selectedWinners = await MarathonApiClient().getSelectedWinner(marathonId: marathonDetailModel.id!);
    if (selectedWinners != null) {
      selectedWinners!.removeWhere((WinnerModel element) {
        if (element.employeeId == AppState().memberInformationList!.eMPLOYEENUMBER) {
          iAmWinner = true;
          return true;
        } else {
          return false;
        }
      });
    }

    notifyListeners();
  }

  Future<void> callNextQuestionApi() async {
    if (currentQuestionNumber < (AppState().getIsDemoMarathon ? demoMarathonDetailModel.totalQuestions! : marathonDetailModel.totalQuestions!)) {
      if (currentQuestionNumber == 0) {
        if (!AppState().getIsDemoMarathon) {
          Utils.showLoading(AppRoutes.navigatorKey.currentContext!);
        }
        currentQuestion = AppState().getIsDemoMarathon
            ? await DemoMarathonRepo().getDemoNextQuestion(currentQuestionNumber: currentQuestionNumber)
            : (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!));
        gapTimeImage = currentQuestion.gapImage;
        gapTimeText = currentQuestion.gapText;
        gapTimeType = currentQuestion.gapType;
        if (Utils.isLoading) {
          Utils.hideLoading(AppRoutes.navigatorKey.currentContext!);
        }
        startTimerForQuestion();
        updateCardData();
        Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen);
      } else {
        currentQuestion = AppState().getIsDemoMarathon
            ? await DemoMarathonRepo().getDemoNextQuestion(currentQuestionNumber: currentQuestionNumber)
            : (await MarathonApiClient().getNextQuestion(questionId: currentQuestion.id, marathonId: marathonDetailModel.id!));
      }
      notifyListeners();
    }
  }

  void updateCardData() {
    if (currentQuestionNumber > 0) {
      swiperController.swipeLeft();
    }
    selectedOptionIndex = null;
    currentQuestionNumber++;

    cardContentList.add(const CardContent());
    totalCurrentQuestionTime = currentQuestion.questionTime! + currentQuestion.nextQuestGap!;
    currentGapTime = currentQuestion.nextQuestGap!;
    totalMarathoners = currentQuestion.remainingParticipantCount!;
    questionCardStatus = QuestionCardStatus.question;
    notifyListeners();
  }

  void populateQuestionStatusesList() {
    answerStatusesList.clear();
    if (demoMarathonDetailModel.totalQuestions != null) {
      for (int i = 0; i < demoMarathonDetailModel.totalQuestions!; i++) {
        answerStatusesList.add(QuestionCardStatus.question);
      }
      notifyListeners();
    }
    if (isUpComingMarathon) {
      if (marathonDetailModel.totalQuestions != null) {
        for (int i = 0; i < marathonDetailModel.totalQuestions!; i++) {
          answerStatusesList.add(QuestionCardStatus.question);
        }
        notifyListeners();
      }
    }
  }

  void updateAnswerStatusesList(QuestionCardStatus status) {
    answerStatusesList[currentQuestionNumber - 1] = status;
    notifyListeners();
  }

  void addItemToList(CardContent value) {
    cardContentList.add(value);
    notifyListeners();
  }

  void updateCurrentQuestionOptionStatus(QuestionsOptionStatus status, int index) {
    for (int i = 0; i < currentQuestion.questionOptions!.length; i++) {
      currentQuestion.questionOptions![i].optionStatus = QuestionsOptionStatus.unSelected;
    }
    currentQuestion.questionOptions![index].optionStatus = status;
    selectedOptionId = currentQuestion.questionOptions![index].id;
    selectedOptionIndex = index;
    notifyListeners();
  }

  void updateQuestionCardStatus(QuestionCardStatus status) {
    if (status == QuestionCardStatus.wrongAnswer || status == QuestionCardStatus.skippedAnswer) {
      if (AppState().getIsDemoMarathon) {
        updateIsUserOutOfGame = true;
      }
    }
    questionCardStatus = status;
    notifyListeners();
  }

  void getCorrectAnswerAndUpdateAnswerColor() {
    if (selectedOptionIndex != null) {
      scheduleMicrotask(() async {
        if (AppState().getIsDemoMarathon) {
          if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) {
            updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!);
          } else {
            updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!);
          }
        } else {
          await callSubmitOptionApi().then((bool value) async {
            updateIsUserOutOfGame = !value;
            if (value) {
              updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!);
            } else {
              updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!);
            }
          });
        }
      });
    } else {
      if (!AppState().getIsDemoMarathon) {
        scheduleMicrotask(() async {
          await callSubmitOptionApi().then((bool value) async {
            updateIsUserOutOfGame = !value;
          });
        });
      }
    }
  }

  void updateCardStatusToAnswer() {
    if (currentQuestionNumber == 0) {
      return;
    }

    scheduleMicrotask(() async {
      await callNextQuestionApi();
    });

    if (selectedOptionIndex == null) {
      updateQuestionCardStatus(QuestionCardStatus.skippedAnswer);
      updateAnswerStatusesList(QuestionCardStatus.skippedAnswer);
      return;
    }
    if (AppState().getIsDemoMarathon) {
      if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) {
        updateQuestionCardStatus(QuestionCardStatus.correctAnswer);
        updateAnswerStatusesList(QuestionCardStatus.correctAnswer);
      } else {
        updateQuestionCardStatus(QuestionCardStatus.wrongAnswer);
        updateAnswerStatusesList(QuestionCardStatus.wrongAnswer);
      }
      return;
    }
    if (!isUserOutOfGame) {
      updateQuestionCardStatus(QuestionCardStatus.correctAnswer);
      updateAnswerStatusesList(QuestionCardStatus.correctAnswer);
      return;
    }
    updateQuestionCardStatus(QuestionCardStatus.wrongAnswer);
    updateAnswerStatusesList(QuestionCardStatus.wrongAnswer);
  }

  void resetValues() async {
    _currentQuestionNumber = 0;
    cardContentList.clear();
    itsMarathonTime = false;
    timerForWinnerSelection.cancel();
    timerForSponsorVideo.cancel();
    timerToWaitForMarathon.cancel();
    timerForQuestion.cancel();
    _isMarathonCompleted = false;
    isUserOutOfGame = false;
    isButtonEnabled = false;
    isUserWaiting = false;
    canPlayDemo = true;
    totalCurrentQuestionTime = 0;
    sponsorsSecondsCounter = 0;
    totalSponsorVideoSeconds = 0;
    totalSecondsToWaitForWinner = 30;
    totalSecondsToWaitForMarathon = 30;
    AppState().setIsDemoMarathon = false;
    currentGapTime = 0;
    currentQuestion = QuestionModel();
    if (answerStatusesList.isNotEmpty) {
      for (int i = 0; i < answerStatusesList.length; i++) {
        answerStatusesList[i] = QuestionCardStatus.question;
      }
    }
    notifyListeners();
  }

  void cancelTimer() {
    timerForQuestion.cancel();
    notifyListeners();
  }

  bool checkIfPrivilegedForMarathon() {
    for (PrivilegeListModel element in AppState().privilegeListModel!) {
      if (element.serviceName == "Marathon") {
        if (element.previlege != null) {
          return element.previlege!;
        }
      }
    }
    return false;
  }

  Future<void> getMarathonDetailsFromApi() async {
    isLoading = true;
    notifyListeners();
    isPrivilegedWithMarathon = checkIfPrivilegedForMarathon();
    demoMarathonDetailModel = await DemoMarathonRepo().getDemoMarathonDetails();

    if (isPrivilegedWithMarathon) {
      marathonDetailModel = await MarathonApiClient().getMarathonDetails();
      updateTotalSecondsToWaitForMarathon = marathonDetailModel.marathonBufferTime ?? 30;
      if (marathonDetailModel.id == null) {
        isUpComingMarathon = false;
        isLoading = false;
        populateQuestionStatusesList();
        notifyListeners();
        return;
      }
      isUpComingMarathon = true;
      if (DateTime.parse(marathonDetailModel.startTime!).isAfter(DateTime.now())) {
        itsMarathonTime = false;
      }
      populateQuestionStatusesList();
    }
    isLoading = false;
    notifyListeners();
  }

  Future<void> onJoinMarathonPressed(BuildContext context) async {
    try {
      isUserWaiting = true;
      Navigator.pushReplacementNamed(context, AppRoutes.marathonWaitingScreen);
    } catch (e) {
      Utils.confirmDialog(context, e.toString());
    }
  }

  Future<void> onJoinDemoMarathonPressed(BuildContext context) async {
    AppState().setIsDemoMarathon = true;
    await callNextQuestionApi();
  }
}
