import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'dart:io';

import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import 'package:just_audio/just_audio.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:rxdart/rxdart.dart';
import 'package:voice_buddies/app/modules/home/controllers/home_controller.dart';
import 'package:voice_buddies/app/widget/base_controller.dart';
import 'package:voice_buddies/app/widget/common_seekbar.dart';

import '../../../models/login_model.dart';
import '../../../models/message_model.dart';
import '../../../utils/all_imports.dart';

class MessageController extends BaseController {
  final recordingPlayer = AudioPlayer();
  AudioPlayer player = AudioPlayer();
  final playerInitial = AudioPlayer();
  // ScrollController scrollController = ScrollController();
  RxBool isNoData = false.obs;
  RxInt currentIndex = 1000000000000.obs;
  MessageModel? messageModel;
  List<MessageData> messageList = <MessageData>[].obs;
  RxString userName = "".obs;
  RxString messageUserId = "".obs;
  int isDelete = 0;

  /// record
  String? path;
  RxBool isRecorded = false.obs;
  Directory? appDirectory;
  bool isRecording = false;
  RecorderController? recorderController;
  PlayerController? playerController;
  File? file;
  Duration maxDuration = const Duration();
  Timer? timer;

  RefreshController refreshController = RefreshController(initialRefresh: false);

  @override
  void onInit() {
    getUserData();
    if (Get.arguments != null) {
      userName.value = Get.arguments[0];
      messageUserId.value = Get.arguments[1];
      isDelete = Get.arguments[2];
    }
    getMessage();

    getDir();
    initialiseControllers();
    playerController = PlayerController();
    super.onInit();
  }

  getMessage() async {
    await getMessageList();
    await getNewMessage();
  }

  /// sendChatMediaApi
  sendChatMediaApi() async {
    FormData formData = FormData.fromMap({
      "media": MultipartFile.fromFileSync(path!, filename: path!.split("/").last),
      "user_id": loginData?.userId ?? 0,
      "receiver_id": messageUserId.value,
    });

    final data = await APIFunction().apiCall(
      apiName: Constants.sendChatMedia,
      params: formData,
      token: loginData?.token,
    );

    LoginDataModel model = LoginDataModel.fromJson(data);

    if (model.responseCode == 1) {
      sendNewMessage(
        message: model.data?.name,
        senderId: loginData?.userId.toString(),
        receiverId: messageUserId.value,
        createdAt: DateTime.now().toUtc().toString().split(".").first,
      );
    } else {
      utils.showSnackBar(message: model.responseMsg!, statusCode: model.responseCode);
    }
  }

  /// msgDeleteApi
  msgDeleteApi({messageIndex, messageId}) async {
    FormData formData = FormData.fromMap({"msg_id": messageId});

    final data = await APIFunction().apiCall(
      apiName: Constants.msgDelete,
      params: formData,
      token: loginData?.token,
    );

    LoginDataModel model = LoginDataModel.fromJson(data);

    if (model.responseCode == 1) {
      // if (messageList.length - 1 > messageShowLength.value - 1) {
      // } else {
      //   messageShowLength.value = messageShowLength.value - 1;
      // }

      messageList.removeAt(messageIndex);
      --currentIndex.value;

      update();
      utils.showToast(message: AppStrings.messageDeleted);
    } else {
      utils.showSnackBar(message: model.responseMsg!, statusCode: model.responseCode);
    }
  }

  ///  unBlockUserApi
  unBlockUserApi({String? blockedUserId}) async {
    FormData formData = FormData.fromMap({
      "user_id": loginData?.userId,
      "user_to": blockedUserId,
    });
    final data = await APIFunction().apiCall(
      apiName: Constants.blockUnBlockUser,
      params: formData,
      token: loginData?.token,
    );
    LoginDataModel model = LoginDataModel.fromJson(data);
    if (model.responseCode == 1) {
      Get.find<HomeController>().getMessageList();
      playerInitial.stop();
      player.stop();
      recordingPlayer.stop();
      Get.back();
      utils.showToast(message: model.responseMsg!);
      update();
    } else {
      utils.showSnackBar(message: model.responseMsg!, statusCode: model.responseCode);
    }
  }

  sendNewMessage({message, senderId, receiverId, createdAt}) async {
    if (Constants.socket!.connected) {
      Constants.socket!.emit(Constants.sendMessage, [message, senderId, receiverId, createdAt]);

      if (senderId == loginData!.userId.toString()) {
        Constants.socket!.emit(Constants.getMessageList, [loginData!.userId, messageUserId.value]);
      }

      printAction("message sent ${[message, senderId, receiverId, createdAt]}");
      utils.showToast(message: AppStrings.yourMessageHasBeenSent);
      deleteFile(File(path!));
      ++currentIndex.value;
      update();
    }
    update();
  }

  Future<void> deleteFile(File file) async {
    try {
      if (await file.exists()) {
        await file.delete();
        recordingPlayer.pause();
        recordingPlayer.seek(Duration.zero);
        isRecorded.value = false;
        update();
      }
    } catch (e) {
      printAction(" Error =-=-=-=-=-=-=-=-=-=-=-=->>${e.toString()}");
    }
  }

  getMessageList() async {
    await getUserData();

    if (Constants.socket!.connected) {
      Constants.socket!.emit(Constants.getMessageList, [loginData!.userId, messageUserId.value]);
      Constants.socket!.on(
        Constants.setMessageList,
        (data) async {
          if (data == null) {
            isNoData.value = true;
            printAction("Data --not message list");
            return;
          } else {
            log("Data ---123--- setMessageList ----->>> ${json.encode(data)}");
            messageModel = MessageModel.fromJson(data);
            messageList = messageModel!.resData!;
          }

          if (messageList.isNotEmpty) {
            isNoData.value = false;
            await Future.delayed(const Duration(milliseconds: 100), () {
              bottomView();
            });
          } else {
            isNoData.value = true;
          }
          update();
        },
      );
    }
  }

  getNewMessage() {
    Constants.socket!.on(
      Constants.setNewMessage,
      (data) async {
        if (data == null) {
          printAction("Data --not received new message");
          return;
        } else {
          log("new message log  ----->>> ${json.encode(data)}");
          MessageData model = MessageData.fromJson(data["resData"]);
          messageList.insert(0, model);
          ++currentIndex.value;
          // messageShowLength.value < 20 ? (messageShowLength.value = messageShowLength.value + 1) : null;
        }

        if (messageList.isNotEmpty) {
          isNoData.value = false;
          await Future.delayed(const Duration(milliseconds: 100), () {
            bottomView();
          });
        }

        update();
      },
    );
  }

  getLocalDate({String? createdAt}) {
    var date = utils.utcToLocal(createdAt!);
    String month = DateFormat.yMMMd('en_US').format(DateTime.parse(date)).split(",").first;
    String hour = DateTime.parse(date).hour.toString().padLeft(2, "0");
    String minutes = DateTime.parse(date).minute.toString().padLeft(2, "0");
    return "$month, $hour:$minutes";
  }

  bottomView() {
    // SchedulerBinding.instance.addPostFrameCallback((_) {
    //   scrollController.animateTo(scrollController.position.minScrollExtent, duration: const Duration(milliseconds: 200), curve: Curves.fastOutSlowIn);
    // });

    update();
  }

  Stream<PositionData> get positionDataStream {
    return Rx.combineLatest3<Duration, Duration, Duration?, PositionData>(
      recordingPlayer.positionStream,
      recordingPlayer.bufferedPositionStream,
      recordingPlayer.durationStream,
      (position, bufferedPosition, duration) => PositionData(
        position,
        bufferedPosition,
        duration ?? Duration.zero,
      ),
    );
  }

  Future<String> getExternalDocumentPath() async {
    var status = await Permission.storage.status;
    if (!status.isGranted) {
      await Permission.storage.request();
    }
    Directory directory = Directory("");
    if (Platform.isAndroid) {
      directory = Directory("/storage/emulated/0/Download");
    } else {
      directory = await getApplicationDocumentsDirectory();
    }
    final exPath = directory.path;
    printAction("Saved Path: $exPath");
    await Directory(exPath).create(recursive: true);
    return exPath;
  }

  Future<String> get _localPath async {
    final String directory = await getExternalDocumentPath();
    return directory;
  }

  download({required String voiceUrl}) async {
    try {
      final path = await _localPath;
      var name = voiceUrl.split("/").last;
      printAction("name of file=$name");
      File file = File('$path/$name');
      var request = await http.get(
        Uri.parse(voiceUrl),
      );
      var bytes = request.bodyBytes;
      await file.writeAsBytes(bytes).then((value) => utils.showToast(message: AppStrings.downloadedSuccessfully));
    } catch (e) {
      printAction("Download error--->>>${e.toString()}");
      utils.showToast(message: AppStrings.downloadFail);
    }
    return;
  }

  startOrStopRecording() async {
    player.playing ? player.pause() : null;
    recordingPlayer.playing ? recordingPlayer.pause() : null;
    if (isRecording) {
      printAction("stopppppppppp record");
      await recorderController!.stop(false);
      isRecording = false;
      stopTimer(resets: false);
      loadRecordingToPlayer();
      update();
    } else {
      playerController?.startPlayer(finishMode: FinishMode.stop);
      playerController?.stopPlayer();
      printAction("starrttttttt record");
      isRecording = true;
      startTimerCount();
      printAction("------isRecordingg------$path>>>>> $isRecording");
      await recorderController!.record(
        path: path!,
        sampleRate: 24000,
        bitRate: 16000,
        androidEncoder: AndroidEncoder.aac,
      );
      utils.showToast(message: AppStrings.recordingStarted);
      update();
      printAction("------------$path>>>>> $path");
    }
    update();
  }

  loadRecordingToPlayer() async {
    utils.showToast(message: AppStrings.recordingStop);
    if (path != null) {
      await recordingPlayer.setAudioSource(AudioSource.file(path!));
      isRecorded.value = true;
      printAction("recording path controller=${path}");

      update();
    } else {
      update();
    }
  }

  void startTimerCount() {
    printAction("timer started");
    timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
  }

  Future<void> addTime() async {
    const addSeconds = 1;

    final seconds = maxDuration.inSeconds + addSeconds;
    if (seconds < 0 || seconds >= 21) {
      await recorderController!.stop(false);
      isRecording = false;
      stopTimer(resets: false);
      loadRecordingToPlayer();
    } else {
      maxDuration = Duration(seconds: seconds);
    }
    printAction(maxDuration.inSeconds.toString());
    update();
  }

  void reset() {
    maxDuration = const Duration();

    update();
  }

  void stopTimer({bool resets = true}) {
    if (resets) {
      reset();
    }
    timer?.cancel();

    update();
  }

  initialiseControllers() {
    recorderController = RecorderController()
      ..androidEncoder = AndroidEncoder.aac
      ..androidOutputFormat = AndroidOutputFormat.mpeg4
      ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
      ..sampleRate = 44100;
  }

  getDir() async {
    appDirectory = await getApplicationDocumentsDirectory();
    path = "${appDirectory!.path}/voiceBuddiesRecording.m4a";

    printAction("--path---------------->> $path");
    checkPermission();
    deleteFile(File(path!));
    update();
  }

  checkPermission() async {
    final status = await Permission.microphone.request();
    printAction("statusstatusstatus");
    if (status.isGranted) {
      printAction("isGranted");
    } else if (status.isPermanentlyDenied) {
      await openAppSettings();
      Get.back();
      printAction("isPermanentlyDenied");
    } else if (status.isDenied) {
      await openAppSettings();
      Get.back();
    }
    update();
  }

  // preparePlayer() async {
  //   playerController!.preparePlayer(path: path!, shouldExtractWaveform: true, volume: 1.0, noOfSamples: 100);
  //   playerController!
  //       .extractWaveformData(
  //     path: path!,
  //   )
  //       .then((waveformData) async {
  //     // debugprintAction(waveformData.toString();
  //     playerController?.startPlayer(finishMode: FinishMode.stop);
  //   });
  // }

  @override
  void onClose() {
    recordingPlayer.stop();
    recordingPlayer.dispose();
    playerInitial.stop();
    playerInitial.dispose();
    timer?.cancel();
    recorderController!.stop(false);
    recorderController!.dispose();
    playerController?.dispose();
    player.stop();
    player.dispose();

    super.onClose();
  }
}
