import 'dart:async';

import 'package:appinio_swiper/appinio_swiper.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mohem_flutter_app/api/marathon/marathon_api_client.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/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();
  List<CardContent> cardContentList = <CardContent>[];
  QuestionModel currentQuestion = QuestionModel();
  List<QuestionCardStatus> answerStatusesList = <QuestionCardStatus>[];
  QuestionCardStatus questionCardStatus = QuestionCardStatus.question;

  int? selectedOptionIndex;
  String? selectedOptionId;
  int totalQualifiers = 0;

  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 _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(marathonDetailModel.sponsors!.first.video!)..initialize();
    videoController = VideoPlayerController.network("http://clips.vorwaerts-gmbh.de/VfE_html5.mp4");
    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();
  }

  //****************TIMERS**********

  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 {
        if (totalSponsorVideoSeconds == 0) {
          timer.cancel();
          notifyListeners();
          return;
        } else {
          totalSponsorVideoSeconds--;
        }

        notifyListeners();
      },
    );
  }

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

  void startTimerToMarathon(BuildContext context) {
    const Duration oneSec = Duration(seconds: 1);
    timerToWaitForMarathon = Timer.periodic(
      oneSec,
      (Timer timer) async {
        if (totalSecondsToWaitForMarathon == 0) {
        } 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 2 seconds and then update card status
        if (totalCurrentQuestionTime - currentGapTime == currentQuestion.questionTime! - 2) {
          getCorrectAnswerAndUpdateAnswerColor();
        }

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

          await callSubmitOptionApi().then((bool value) async {
            if (value) {
              await callNextQuestionApi();
            }
          });
        }

        if (totalCurrentQuestionTime == 0) {
          updateCardData();
          if (currentQuestionNumber == marathonDetailModel.totalQuestions! - 1) {
            updateQuestionCardStatus(QuestionCardStatus.findingWinner);
            timer.cancel();
            cancelTimer();
            notifyListeners();
          }
          return;
        } else {
          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 == 0) {
          timer.cancel();
          updateQuestionCardStatus(QuestionCardStatus.winnerFound);
          return;
        } else {
          totalSecondsToWaitForWinner--;
        }

        notifyListeners();
      },
    );
  }

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

  Future<bool> callSubmitOptionApi() async {
    return await MarathonApiClient().submitSelectedOption(selectedAnswerId: selectedOptionId);
  }

  // TODO: here I need to add a logic where I should call this function for Api but for the 1st question it should behave differently
  // TODO: Verify the callings!!!
  Future<void> callNextQuestionApi() async {
    if (currentQuestionNumber < marathonDetailModel.totalQuestions!) {
      if (currentQuestionNumber == 0) {
        currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: null, marathonId: marathonDetailModel.id!))!;
        if (Utils.isLoading) {
          Utils.hideLoading(AppRoutes.navigatorKey.currentContext!);
        }
        startTimerForQuestion();
        updateCardData();

        Navigator.pushReplacementNamed(AppRoutes.navigatorKey.currentContext!, AppRoutes.marathonScreen);
      } else {
        currentQuestion = (await MarathonApiClient().getNextQuestion(questionId: currentQuestion.id, marathonId: marathonDetailModel.id!))!;
      }
      notifyListeners();
    }
  }

  void updateCardData() {
    if (currentQuestionNumber > 0) {
      print("swiped it away!!");
      swipeCardLeft();
    }
    selectedOptionIndex = null;
    currentQuestionNumber++;
    cardContentList.add(const CardContent());
    totalCurrentQuestionTime = currentQuestion.questionTime! + currentQuestion.nextQuestGap!;
    currentGapTime = currentQuestion.nextQuestGap!;
    totalMarathoners = currentQuestion.remainingParticipantCount!;
    questionCardStatus = QuestionCardStatus.question;
  }

  void populateQuestionStatusesList() {
    if (marathonDetailModel.totalQuestions != null) {
      for (int i = 0; i < marathonDetailModel.totalQuestions! - 1; 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;
    selectedOptionIndex = index;
    notifyListeners();
  }

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

  void getCorrectAnswerAndUpdateAnswerColor() {
    if (selectedOptionIndex != null) {
      if (currentQuestion.questionOptions![selectedOptionIndex!].isCorrectOption!) {
        updateCurrentQuestionOptionStatus(QuestionsOptionStatus.correct, selectedOptionIndex!);
      } else {
        updateCurrentQuestionOptionStatus(QuestionsOptionStatus.wrong, selectedOptionIndex!);
      }
    }
  }

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

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

  void swipeCardLeft() {
    swiperController.swipeLeft();
    notifyListeners();
  }

  void resetValues() async {
    _currentQuestionNumber = 0;
    cardContentList.clear();
    timerForWinnerSelection.cancel();
    timerForQuestion.cancel();
    _isMarathonCompleted = false;
    totalCurrentQuestionTime = 0;
    currentQuestion = QuestionModel();

    notifyListeners();
  }

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

  Future<void> getMarathonDetailsFromApi() async {
    isLoading = true;
    notifyListeners();
    await MarathonApiClient().getMarathonToken().whenComplete(() async {
      marathonDetailModel = await MarathonApiClient().getMarathonDetails();
      if (marathonDetailModel.id == null) {
        isUpComingMarathon = false;
        notifyListeners();
        return;
      }
      populateQuestionStatusesList();
      isLoading = false;
      notifyListeners();
    });
  }

  Future<void> buildConnectionWithSignalR(BuildContext context) async {
    Utils.showLoading(context);
    try {
      resetValues();
      // await MarathonApiClient().buildHubConnection(context, marathonDetailModel.sponsors!.first.sponsorPrizes!.first.id!);
    } catch (e) {
      if (kDebugMode) {
        print("error in buildConnectionWithSignalR: ${e.toString()}");
      }
      Utils.hideLoading(context);
      Utils.confirmDialog(context, e.toString());
    }
  }

  Future<void> onJoinMarathonPressed(BuildContext context) async {
    //TODO: here we need to put a check to make sure we should not display sponsor when remaining time to marathon is less than 30 seconds plus video duration e.g. 30 seconds + video duration time
    // if (marathonDetailModel.sponsors!.first.video != null && marathonDetailModel.sponsors!.first.video != "") {
    if (false) {
      await initializeVideoPlayer().then((_) {
        startTimerForSponsorVideo();
        Navigator.pushNamed(context, AppRoutes.marathonSponsorVideoScreen);
      });
    } else {
      try {
        Utils.showLoading(context);
        bool isJoined = await MarathonApiClient().joinMarathonAsParticipant();
        if (isJoined) {
          print("joined");
          callNextQuestionApi();
        }
      } catch (e, s) {
        Utils.hideLoading(context);
        Utils.confirmDialog(context, e.toString());
      }
    }
  }
}
