// ignore_for_file: body_might_complete_normally_catch_error, avoid_print

import 'dart:async';
import 'dart:convert';
import 'package:didvan/main.dart';
import 'package:didvan/models/ai/files_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart';
import 'package:didvan/models/ai/bots_model.dart';
import 'package:didvan/models/ai/chats_model.dart';
import 'package:didvan/models/ai/messages_model.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/services/ai/ai_api_service.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';

class AiChatState extends CoreProvier {
  ValueNotifier<Stream<String>> messageOnstream =
      ValueNotifier(const Stream<String>.empty().asBroadcastStream());
  List<MessageModel> messages = [];
  bool onResponsing = false;
  bool loading = false;
  bool isEdite = false;
  ValueNotifier<bool> changingPlaceHolder = ValueNotifier(false);
  final ScrollController scrollController = ScrollController();
  int? chatId;
  ChatsModel? chat;
  FilesModel? file;
  bool isRecorded = false;
  TextEditingController message = TextEditingController();

  List<String> suggestedQuestions = [];
  bool isLoadingQuestions = true;

  Future<void> _scrolledEnd() async {
    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await scrollController.animateTo(
        scrollController.position.minScrollExtent,
        duration: const Duration(milliseconds: 600),
        curve: Curves.easeInOut,
      );
    });
  }

  Future<void> _onError(e) async {
    onResponsing = false;
    messages.last.prompts.removeLast();
    messages.last.prompts.last =
        messages.last.prompts.last.copyWith(error: true);
    messageOnstream.value = const Stream.empty();

    await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData(
        message: 'خطا در برقراری ارتباط', aLertType: ALertType.error));
    update();
    print("error is ${e.toString()}");
  }

  Future<RequestService> getChatId() async {
    final service = RequestService(
      RequestHelper.aiChatId(),
    );
    await service.httpGet();
    if (service.isSuccess) {
      final id = service.result['id'];
      chatId = id;
      chat ??= ChatsModel(id: chatId);
    }
    return service;
  }

  Future<void> fetchSuggestedQuestions() async {
    isLoadingQuestions = true;
    suggestedQuestions.clear();
    update();

    final service = RequestService(
      RequestHelper.aiSuggestedQuestions(),
    );

    await service.post();

    if (service.isSuccess) {
      final List<dynamic> questionsList = service.data('Questions');
      if (questionsList.isNotEmpty) {
        suggestedQuestions = questionsList.map((q) => q.toString()).toList();
      }
      isLoadingQuestions = false;
      update();
    } else {
      print("Error fetching suggested questions: ${service.errorMessage}");
      isLoadingQuestions = false;
      update();
    }
  }

  Future<void> getAllMessages(int chatId) async {
    loading = true;
    onResponsing = true;
    update();
    final service = RequestService(
      RequestHelper.aiAChat(chatId),
    );
    await service.httpGet();
    if (service.isSuccess) {
      messages.clear();
      final ChatsModel allMessages =
          ChatsModel.fromJson(service.result['chat']);
      for (var i = 0; i < allMessages.prompts!.length; i++) {
        final prompt = allMessages.prompts![i];
        if (messages.isNotEmpty &&
            DateTime.parse(messages.last.prompts.last.createdAt.toString())
                .toPersianDateStr()
                .contains(DateTime.parse(prompt.createdAt.toString())
                    .toPersianDateStr())) {
          messages.last.prompts.add(prompt);
        } else {
          messages.add(MessageModel(
              dateTime: prompt.createdAt.toString(), prompts: [prompt]));
        }
      }
      appState = AppState.idle;
      loading = false;
      onResponsing = false;
      update();
      return;
    }
    appState = AppState.failed;
    loading = false;
    onResponsing = false;
    update();
  }

  Future<void> changePlaceHolder(String placeholder) async {
    changingPlaceHolder.value = true;
    update();
    await Future.delayed(const Duration(seconds: 3));

    final service = RequestService(RequestHelper.placeholder(chatId!),
        body: {'placeholder': placeholder});
    await service.put();
    if (service.isSuccess) {
      appState = AppState.idle;
      if (chat == null) {
        chat = ChatsModel(id: chatId, placeholder: placeholder);
      } else {
        chat = chat!.copyWith(placeholder: placeholder);
      }
      changingPlaceHolder.value = false;
      update();
      return;
    }
    appState = AppState.failed;
    changingPlaceHolder.value = false;
    update();
  }

  Future<void> postMessage(BotsModel bot, bool isAssistants) async {
    onResponsing = true;
    final uploadedFile = file;
    file = null;
    update();
    String message = messages.last.prompts.last.text!;

    messages.last.prompts.add(Prompts(
        finished: false,
        error: false,
        text: '',
        role: 'bot',
        createdAt: DateTime.now()
            .subtract(const Duration(minutes: 210))
            .toIso8601String()));
    update();
    await _scrolledEnd();

    String url;
    if (bot.id == 100) {
      url = '/100/aisummery';
    } else if (bot.id == 101) {
      url = '/101/aiaudio';
    } else if (bot.id == 27) {
      url = '/27/chart-analysis';
    } else if (bot.id == 102) {
      url = '/102/aivideo';
    } else {
      url =
          '${isAssistants ? '/user/${bot.responseType}' : ''}/${bot.id}/${bot.name}'
              .toLowerCase();
    }

    final req = await AiApiService.initial(
        url: url,
        message: message,
        chatId: chatId,
        file: uploadedFile,
        edite: isEdite);
    file = null;
    isRecorded = false;
    update();

    final res = await AiApiService().getResponse(req).catchError((e) {
      _onError(e);
      // return e;
    });

    String responseMessgae = '';
    String dataMessgae = '';
    update();

    final r = res.listen((value) async {
      var str = utf8.decode(value);

      if (!kIsWeb) {
        if (bot.responseType != 'text') {
          responseMessgae += str.split('{{{').first;
        }
        if (str.contains('{{{')) {
          dataMessgae += "{{{${str.split('{{{').last}";
          update();
          return;
        }
      }

      if (bot.id == 101) {
        responseMessgae += str;
        return;
      }

      if (bot.id == 102) {
        responseMessgae += str;
        return;
      }

      responseMessgae += str;

      if (kIsWeb) {
        try {
          int startIndex = responseMessgae.indexOf('{{{');

          String slicedText =
              responseMessgae.substring(startIndex, responseMessgae.length);
          dataMessgae = slicedText;
          responseMessgae = responseMessgae.replaceAll(dataMessgae, '');
        } catch (e) {
          e.printError();
        }
        if (bot.responseType != 'text') {
          responseMessgae = "${responseMessgae.split('.png').first}.png";
          return;
        }
      }
      messageOnstream.value = Stream.value(responseMessgae);

      update();
    });

    r.onDone(() async {
      if (chatId == null) {
        final service = await getChatId();
        navigatorKey.currentContext!.read<HistoryAiChatState>().getChats();
        if (!service.isSuccess) {
          _onError(null);
          return;
        }
      }
      onResponsing = false;
      if (responseMessgae.isEmpty) {
        messages.last.prompts.removeLast();
        messages.last.prompts.last =
            messages.last.prompts.last.copyWith(error: true);
        messageOnstream.value = const Stream.empty();
        update();
        _scrolledEnd();
        return;
      } else {
        int? humanMessageId;
        int? aiMessageId;
        String? audioUrl;
        String? videoUrl;

        if (bot.id == 101) {
          try {
            String jsonString = responseMessgae;

            if (jsonString.contains('}{')) {
              jsonString =
                  jsonString.substring(0, jsonString.indexOf('}{') + 1);
            }

            final jsonData = json.decode(jsonString);

            audioUrl = jsonData['url']?.toString();
            humanMessageId = jsonData['HUMAN_MESSAGE_ID'];
            aiMessageId = jsonData['AI_MESSAGE_ID'];
          } catch (e) {
            e.printError();
            return;
          }
        } else if (bot.id == 102) {
          try {
            String jsonString = responseMessgae;

            if (jsonString.contains('}{')) {
              jsonString =
                  jsonString.substring(0, jsonString.indexOf('}{') + 1);
            }

            final jsonData = json.decode(jsonString);

            videoUrl = jsonData['url']?.toString();
            humanMessageId = jsonData['HUMAN_MESSAGE_ID'];
            aiMessageId = jsonData['AI_MESSAGE_ID'];
          } catch (e) {
            e.printError();
            return;
          }
        } else {
          try {
            final data = AppInitializer.messagesData(dataMessgae);
            humanMessageId = data['HUMAN_MESSAGE_ID'];
            aiMessageId = data['AI_MESSAGE_ID'];
          } catch (e) {
            e.printError();
            return;
          }
        }

        messages.last.prompts.last = messages.last.prompts.last.copyWith(
            finished: true,
            text: bot.responseType != 'text' && bot.id != 101 && bot.id != 102
                ? null
                : (bot.id == 101 || bot.id == 102 ? null : responseMessgae),
            file: bot.responseType != 'text' || bot.id == 101 || bot.id == 102
                ? (bot.id == 101
                    ? audioUrl
                    : (bot.id == 102 ? videoUrl : responseMessgae))
                : null,
            audio: bot.id == 101 ? true : null,
            video: bot.id == 102 ? true : null,
            id: aiMessageId);
        if (messages.last.prompts.length > 2) {
          messages.last.prompts[messages.last.prompts.length - 2] = messages
              .last.prompts[messages.last.prompts.length - 2]
              .copyWith(id: humanMessageId);
        } else {
          messages.last.prompts.first =
              messages.last.prompts.first.copyWith(id: humanMessageId);
        }
        messageOnstream.value = const Stream.empty();
      }

      if (isEdite) {
        messages.last.prompts.removeAt((messages.last.prompts.length - 1) - 2);
        messages.last.prompts.removeAt((messages.last.prompts.length - 1) - 2);
        isEdite = false;
      }

      update();
      _scrolledEnd();
    });

    r.onError((e) => _onError(e));
  }

  Future<void> deleteMessage(int id, int mIndex, int index) async {
    final service = RequestService(RequestHelper.deleteMessage(chatId!, id));
    await service.delete();
    if (service.isSuccess) {
      if (messages[mIndex].prompts.length <= 1) {
        messages.removeAt(mIndex);
      } else {
        messages[mIndex].prompts.removeAt(index);
      }
      appState = AppState.idle;
      update();
      return;
    }
    appState = AppState.failed;
    await ActionSheetUtils(navigatorKey.currentContext!).showAlert(AlertData(
        message: 'خطا در برقراری ارتباط', aLertType: ALertType.error));
    update();
  }

  Future<void> clearChat() async {
    messages.clear();
    chatId = null;
    chat = null;
    file = null;
    message.clear();
    isEdite = false;
    onResponsing = false;
    loading = false;
    notifyListeners();
    await Future.delayed(const Duration(milliseconds: 10));
  }
}
