import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:logger/logger.dart' as L;
import 'package:logging/logging.dart';
import 'package:mohem_flutter_app/api/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/models/marathon/marathon_generic_model.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/marathon_provider.dart';
import 'package:provider/provider.dart';
import 'package:signalr_netcore/http_connection_options.dart';
import 'package:signalr_netcore/hub_connection.dart';
import 'package:signalr_netcore/hub_connection_builder.dart';

class MarathonApiClient {
  Future<String> getMarathonToken() async {
    String employeeUserName = AppState().getUserName ?? "";
    String employeeSession = AppState().postParamsObject?.pSessionId.toString() ?? "";

    Map<String, String> jsonObject = <String, String>{"userName": employeeUserName, "password": employeeSession};
    Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonParticipantLoginUrl, jsonObject);

    var json = jsonDecode(response.body);

    MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);

    if (marathonModel.statusCode == 200) {
      if (marathonModel.data != null && marathonModel.isSuccessful == true) {
        AppState().setMarathonToken = marathonModel.data["token"] ?? "";
        print("bearer: ${AppState().getMarathonToken}");
        return marathonModel.data["token"] ?? "";
      } else {
        //TODO : DO ERROR HANDLING HERE
        return "";
      }
    } else {
      //TODO : DO ERROR HANDLING HERE
      return "";
    }
  }

  Future<String> getProjectId() async {
    Response response = await ApiClient().postJsonForResponse(ApiConsts.marathonProjectGetUrl, <String, dynamic>{}, token: AppState().getMarathonToken ?? await getMarathonToken());

    var json = jsonDecode(response.body);
    MarathonGenericModel marathonModel = MarathonGenericModel.fromJson(json);

    if (marathonModel.statusCode == 200) {
      if (marathonModel.data != null && marathonModel.isSuccessful == true) {
        logger.i("message: ${marathonModel.data[0]["id"]}");
        AppState().setMarathonProjectId = marathonModel.data[0]["id"] ?? "";
        return marathonModel.data[0]["id"] ?? "";
      } else {
        //TODO : DO ERROR HANDLING HERE
        return "";
      }
    } else {
      //TODO : DO ERROR HANDLING HERE
      return "";
    }
  }

  Future<MarathonDetailModel> getMarathonDetails() async {
    String payrollString = AppState().postParamsObject?.payrollCodeStr.toString() ?? "CS";

    Response response = await ApiClient().getJsonForResponse(ApiConsts.marathonUpcomingUrl + payrollString, token: AppState().getMarathonToken ?? await getMarathonToken());

    var json = jsonDecode(response.body);

    MarathonGenericModel marathonGenericModel = MarathonGenericModel.fromJson(json);

    MarathonDetailModel marathonDetailModel = MarathonDetailModel.fromJson(marathonGenericModel.data);

    AppState().setMarathonProjectId = marathonDetailModel.id!;

    return marathonDetailModel;
  }

  late HubConnection hubConnection;
  L.Logger logger = L.Logger();

  Future<void> buildHubConnection(BuildContext context) async {
    HttpConnectionOptions httpOptions = HttpConnectionOptions(skipNegotiation: false, logMessageContent: true);
    hubConnection = HubConnectionBuilder()
        .withUrl(
          ApiConsts.marathonHubConnectionUrl + "?employeeNumber=${AppState().memberInformationList!.eMPLOYEENUMBER ?? ""}&employeeName=${AppState().memberInformationList!.eMPLOYEENAME ?? ""}",
          options: httpOptions,
        )
        .withAutomaticReconnect(
          retryDelays: <int>[2000, 5000, 10000, 20000],
        )
        .configureLogging(
          Logger("Logging"),
        )
        .build();
    hubConnection.onclose(
      ({Exception? error}) {
        logger.i("onclose");
      },
    );
    hubConnection.onreconnecting(
      ({Exception? error}) {
        logger.i("onreconnecting");
      },
    );
    hubConnection.onreconnected(
      ({String? connectionId}) {
        logger.i("onreconnected");
      },
    );
    if (hubConnection.state != HubConnectionState.Connected) {
      await hubConnection.start();
      logger.i("Started HubConnection");

      await hubConnection.invoke(
        "AddParticipant",
        args: <Object>[
          <String, dynamic>{
            "employeeNumber": AppState().memberInformationList!.eMPLOYEENUMBER ?? "",
            "employeeName": AppState().memberInformationList!.eMPLOYEENAME ?? "",
            "marathonId": AppState().getMarathonProjectId,
          }
        ],
      ).catchError((e) {
        logger.i("Error in AddParticipant: $e");
      });

      context.read<MarathonProvider>().addItemToList(ApiConsts.dummyQuestion);

      await hubConnection.invoke(
        "SendQuestionToParticipant",
        args: <Object>[
          <String, dynamic>{
            "marathonId": "${AppState().getMarathonProjectId}",
          }
        ],
      ).catchError((e) {
        logger.i("Error in SendQuestionToParticipant: $e");
      });

      try {
        hubConnection.on("OnSendQuestionToParticipant", (List<Object?>? arguments) {
          onSendQuestionToParticipant(arguments, context);
        });
      } catch (e, s) {
        logger.i("Error in OnSendQuestionToParticipant");
      }

      try {
        hubConnection.on("OnParticipantJoin", (List<Object?>? arguments) {
          onParticipantJoin(arguments, context);
        });
      } catch (e, s) {
        logger.i("Error in OnParticipantJoin");
      }
    }
  }

  Future<void> onSendQuestionToParticipant(List<Object?>? arguments, BuildContext context) async {
    logger.i("onSendQuestionToParticipant arguments: $arguments");

    if (arguments != null) {
      Map<dynamic, dynamic> data = arguments.first! as Map<dynamic, dynamic>;
      var json = data["data"];
      QuestionModel newQuestion = QuestionModel.fromJson(json);
      context.read<MarathonProvider>().onNewQuestionReceived(newQuestion);
    }
  }

  Future<void> onParticipantJoin(List<Object?>? arguments, BuildContext context) async {
    logger.i("OnParticipantJoin arguments: $arguments");
    context.watch<MarathonProvider>().totalMarathoners++;
  }
}
