import 'dart:async';
import 'dart:io';

import 'package:dio/dio.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import '../../../../core/constants/url_services_constant.dart';
import '../../../../core/extensions/log_extension.dart';
import '../../../../data/repo/message_repository.dart';
import '../../../auth/login/cubit/login_cubit.dart';
import '../../cubit/message_cubit.dart';
import '../../model/query/req_fetch_group.dart';
import '../../model/query/req_fetch_message.dart';
import '../../model/query/req_seen_update_group.dart';
import '../../model/query/req_send_message.dart';
import '../../model/query/req_upload_file.dart';
import '../../model/upload_file.dart';
import '../../utilities/debouncer.dart';
import '../chat_view.dart';

mixin ChatViewMixin on State<ChatView> {
  final TextEditingController _messageController = TextEditingController();

  final GlobalKey<FormState> messageKey = GlobalKey();

  ValueNotifier<String> textChanged = ValueNotifier("");
  ValueNotifier<bool> sendButtonActive = ValueNotifier(false);

  TextEditingController get messageController => _messageController;

  // TODO: chat cubit yerine message cubit kullanılarak aynı işlemler denenecek. çünkü grup listesini yeniden çekme gibi işlemler orada yapıldı

  late final MessageCubit _messageCubit;
  MessageCubit get messageCubitInstance => _messageCubit;

  late ScrollController _scrollController;
  final Debouncer _debouncer = Debouncer(milliseconds: 650);
  ScrollController get scrollController => _scrollController;

  ValueNotifier<String> fileName = ValueNotifier("");



  ValueNotifier<double> uploadProgress = ValueNotifier(0.00);

  FilePickerResult? result;

  Future<void> pickFile();
  void customShowBottomSheet();

  IconData getFileIcon() {
    String extension = fileName.value.split('.').last.toLowerCase();

    switch (extension) {
      case 'pdf':
        return Icons.picture_as_pdf;
      case 'jpg':
      case 'jpeg':
      case 'png':
        return Icons.image;
      // Diğer uzantılar için varsayılan dosya ikonu
      default:
        return Icons.file_copy;
    }
  }

  void resetFile() {
    fileName.value = '';
    result = null;
  }

  Future<String> uploadFileToServer(ReqUploadFile model) async {
    try {
      var url = URLAPIService.instance.fileUploadURL;

      final FormData data = FormData.fromMap({
        "loginuserid": model.loginuserid,
        "messagefile": await MultipartFile.fromFile(model.filePath,
            filename: model.fileName),
      });

      final response = await Dio(BaseOptions(baseUrl: URLAPIService.baseURL))
          .post(url,
              data: data,
              onSendProgress: (count, total) =>
                  uploadProgress.value = count / total);
      if (response.statusCode == 200) {
        'result file : ${response.data}'.log();

        if (response.data is Map<String, dynamic>?) {
          'return response : ${UploadFile.fromJson(response.data).mesajformat}'
              .log();
          return UploadFile.fromJson(response.data).mesajformat ?? '';
        }
      }
      return '';
    } catch (_) {
      throw Exception('${model.fileName} dosya yüklenemedi');
    }
  }

  @override
  void initState() {
    _messageCubit = MessageCubit(MessageRepository());
    initialProcess();
    _scrollController = ScrollController();
    _scrollController.addListener(_onScroll);
    super.initState();
  }

  @override
  void dispose() {
    _scrollController
      ..addListener(_onScroll)
      ..dispose();
    _messageController.dispose();
    super.dispose();
  }

  void initialProcess() {
    widget.cubit.lastSeenUpdate(ReqSeenUpdateGroup(
        loginuserid: context.read<LoginCubit>().id,
        token: context.read<LoginCubit>().token,
        grupUserfirmid: context.read<LoginCubit>().userfirmId,
        grupId: widget.model?.grupId ?? "0"));
  }

  void fetchLastMessage() {
    messageCubitInstance.fetchLastMessageList(ReqFetchMessage(
        loginuserid: context.read<LoginCubit>().id,
        token: context.read<LoginCubit>().token,
        userfirmid: context.read<LoginCubit>().userfirmId,
        grupId: widget.model!.grupId ?? ""));
  }

  void _onScroll() async {
    _debouncer.run(() {
      if (_isTop) {
        _messageCubit.fetchPaginationMessageList(ReqFetchMessage(
            loginuserid: context.read<LoginCubit>().id,
            token: context.read<LoginCubit>().token,
            userfirmid: context.read<LoginCubit>().userfirmId,
            grupId: widget.model!.grupId ?? ""));
      }
    });
  }

  bool get _isTop {
    if (!_scrollController.hasClients) return false;

    /// - Scroll son pozisyonunu alıyorum
    final maxScroll = _scrollController.position.maxScrollExtent;

    /// - Mevcut scroll pozisyonunu alıyorum
    final currentScroll = _scrollController.offset;

    'current scroll : $currentScroll'.log();

    /// - Benim mevcut pozisyonum maxScrollun %90'ını geçmiş ise diğer sayfa getirilmeye başlansın yani true dönecek, küçük ise zaten false
    return currentScroll >= (maxScroll * .95);
  }

  void textAreaChanged(String text) {
    'textChanged calisti : $text'.log();
    textChanged.value = text;
  }

  Future<void> sendMessage() async {
    /// - Burada mesajı gönderip fetchMessagePaginationList ile bu sayfada yeni listeyi görmek istediğimden bu sayfada oluşturduğum instance üzerinden istek atıyorum

    final isFileName = await checkSelectFileAndToMesssage();
    if (!mounted) return;

    if (isFileName != null) {
      _messageCubit
          .sendMessageToGroup(ReqSendMessage(
              loginuserid: context.read<LoginCubit>().id,
              token: context.read<LoginCubit>().token,
              userfirmid: context.read<LoginCubit>().userfirmId,
              grupId: widget.model!.grupId ?? "",
              mesaj: checkMessageTxt(isFileName),
              mesajDosyalar: isFileName))
          .then((value) => fetchNewGroupListAndTxtFieldClear());
    } else {
      _messageCubit
          .sendMessageToGroup(ReqSendMessage(
              loginuserid: context.read<LoginCubit>().id,
              token: context.read<LoginCubit>().token,
              userfirmid: context.read<LoginCubit>().userfirmId,
              grupId: widget.model!.grupId ?? "",
              mesaj: messageController.text,
              mesajDosyalar: null))
          .then((value) => fetchNewGroupListAndTxtFieldClear());
    }
  }

  String checkMessageTxt(String formatUrl) {
    if (messageController.text.isNotEmpty) return messageController.text;
    if (formatUrl.isEmpty) return 'Dosya';
    return 'Dosya';
  }

  Future<String?> checkSelectFileAndToMesssage() async {
    if (result == null) return null;

    File file = File(result?.files.single.path ?? " ");
    String filename = file.path.split('/').last;
    String filepath = file.path;

    final formatUrl = await uploadFileToServer(ReqUploadFile(
        filePath: filepath,
        fileName: filename,
        loginuserid: context.read<LoginCubit>().id));

    if (formatUrl.isNotEmpty) {
      if (!mounted) return null;

      /// Format url return
      return formatUrl;
    }
    return null;
  }

  /// TODO: Tekrar tekrar dosya gönderme problemi tekrar test edilsin....
  /// 16.03.24

  void fetchNewGroupListAndTxtFieldClear() {
    /// - Burada da önceki sayfanın da son mesajdan haberdar olması için dışarıdan parametre olarak aldığımız cubit üzerinden fetchGroupList metodunu tetikliyorum
    widget.cubit.fetchGroupList(ReqFetchGroup(
        loginuserid: context.read<LoginCubit>().id,
        token: context.read<LoginCubit>().token,
        userfirmid: context.read<LoginCubit>().userfirmId));

    // son mesajlar çekilecek
    fetchLastMessage();

    /// bu sayede dosya seçimi sıfırlanacak.
    resetFile();

    // - Son yazılanı sil
    _messageController.text = "";

    // - Klavyeyi kapat
    FocusScope.of(context).unfocus();
  }

  /// - Mesaj listesini en sona kaydırmak için kullanılır
  void scrollAnimateToMaxExtent() async {
    _scrollController.animateTo(
      _scrollController.position.maxScrollExtent,
      duration: const Duration(milliseconds: 600),
      curve: Curves.easeIn,
    );
  }
}
