import 'package:get/get.dart';
import 'package:marco/helpers/services/api_service.dart';
import 'package:marco/helpers/services/app_logger.dart';
import 'package:marco/model/directory/contact_model.dart';
import 'package:marco/model/directory/contact_bucket_list_model.dart';
import 'package:marco/model/directory/directory_comment_model.dart';
import 'package:marco/helpers/widgets/my_snackbar.dart';

class DirectoryController extends GetxController {
  RxList<ContactModel> allContacts = <ContactModel>[].obs;
  RxList<ContactModel> filteredContacts = <ContactModel>[].obs;
  RxList<ContactCategory> contactCategories = <ContactCategory>[].obs;
  RxList<String> selectedCategories = <String>[].obs;
  RxList<String> selectedBuckets = <String>[].obs;
  RxBool isActive = true.obs;
  RxBool isLoading = false.obs;
  RxList<ContactBucket> contactBuckets = <ContactBucket>[].obs;
  RxString searchQuery = ''.obs;
  RxBool showFabMenu = false.obs;
  final RxBool showFullEditorToolbar = false.obs;
  final RxBool isEditorFocused = false.obs;
  RxBool isNotesView = false.obs;

  final Map<String, RxList<DirectoryComment>> contactCommentsMap = {};
  RxList<DirectoryComment> getCommentsForContact(String contactId) {
    return contactCommentsMap[contactId] ?? <DirectoryComment>[].obs;
  }

  final editingCommentId = Rxn<String>();

  @override
  void onInit() {
    super.onInit();
    fetchContacts();
    fetchBuckets();
  }
// inside DirectoryController

  Future<void> updateComment(DirectoryComment comment) async {
    try {
      logSafe(
          "Attempting to update comment. id: ${comment.id}, contactId: ${comment.contactId}");

      final commentList = contactCommentsMap[comment.contactId];
      final oldComment =
          commentList?.firstWhereOrNull((c) => c.id == comment.id);

      if (oldComment == null) {
        logSafe("Old comment not found. id: ${comment.id}");
      } else {
        logSafe("Old comment note: ${oldComment.note}");
        logSafe("New comment note: ${comment.note}");
      }

      if (oldComment != null && oldComment.note.trim() == comment.note.trim()) {
        logSafe("No changes detected in comment. id: ${comment.id}");
        showAppSnackbar(
          title: "No Changes",
          message: "No changes were made to the comment.",
          type: SnackbarType.info,
        );
        return;
      }

      final success = await ApiService.updateContactComment(
        comment.id,
        comment.note,
        comment.contactId,
      );

      if (success) {
        logSafe("Comment updated successfully. id: ${comment.id}");
        await fetchCommentsForContact(comment.contactId);

        // ✅ Show success message
        showAppSnackbar(
          title: "Success",
          message: "Comment updated successfully.",
          type: SnackbarType.success,
        );
      } else {
        logSafe("Failed to update comment via API. id: ${comment.id}");
        showAppSnackbar(
          title: "Error",
          message: "Failed to update comment.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stackTrace) {
      logSafe("Update comment failed: ${e.toString()}");
      logSafe("StackTrace: ${stackTrace.toString()}");
      showAppSnackbar(
        title: "Error",
        message: "Failed to update comment.",
        type: SnackbarType.error,
      );
    }
  }

  Future<void> fetchCommentsForContact(String contactId) async {
    try {
      final data = await ApiService.getDirectoryComments(contactId);
      logSafe("Fetched comments for contact $contactId: $data");

      final comments =
          data?.map((e) => DirectoryComment.fromJson(e)).toList() ?? [];

      if (!contactCommentsMap.containsKey(contactId)) {
        contactCommentsMap[contactId] = <DirectoryComment>[].obs;
      }

      contactCommentsMap[contactId]!.assignAll(comments);
      contactCommentsMap[contactId]?.refresh();
    } catch (e) {
      logSafe("Error fetching comments for contact $contactId: $e",
          level: LogLevel.error);

      contactCommentsMap[contactId] ??= <DirectoryComment>[].obs;
      contactCommentsMap[contactId]!.clear();
    }
  }

  Future<void> fetchBuckets() async {
    try {
      final response = await ApiService.getContactBucketList();
      if (response != null && response['data'] is List) {
        final buckets = (response['data'] as List)
            .map((e) => ContactBucket.fromJson(e))
            .toList();
        contactBuckets.assignAll(buckets);
      } else {
        contactBuckets.clear();
      }
    } catch (e) {
      logSafe("Bucket fetch error: $e", level: LogLevel.error);
    }
  }

  Future<void> fetchContacts({bool active = true}) async {
    try {
      isLoading.value = true;

      final response = await ApiService.getDirectoryData(isActive: active);

      if (response != null) {
        final contacts = response.map((e) => ContactModel.fromJson(e)).toList();
        allContacts.assignAll(contacts);
        extractCategoriesFromContacts();
        applyFilters();
      } else {
        allContacts.clear();
        filteredContacts.clear();
      }
    } catch (e) {
      logSafe("Directory fetch error: $e", level: LogLevel.error);
    } finally {
      isLoading.value = false;
    }
  }

  void extractCategoriesFromContacts() {
    final uniqueCategories = <String, ContactCategory>{};

    for (final contact in allContacts) {
      final category = contact.contactCategory;
      if (category != null && !uniqueCategories.containsKey(category.id)) {
        uniqueCategories[category.id] = category;
      }
    }

    contactCategories.value = uniqueCategories.values.toList();
  }

  void applyFilters() {
    final query = searchQuery.value.toLowerCase();

    filteredContacts.value = allContacts.where((contact) {
      final categoryMatch = selectedCategories.isEmpty ||
          (contact.contactCategory != null &&
              selectedCategories.contains(contact.contactCategory!.id));

      final bucketMatch = selectedBuckets.isEmpty ||
          contact.bucketIds.any((id) => selectedBuckets.contains(id));

      final nameMatch = contact.name.toLowerCase().contains(query);
      final orgMatch = contact.organization.toLowerCase().contains(query);
      final emailMatch = contact.contactEmails
          .any((e) => e.emailAddress.toLowerCase().contains(query));
      final phoneMatch = contact.contactPhones
          .any((p) => p.phoneNumber.toLowerCase().contains(query));
      final tagMatch =
          contact.tags.any((tag) => tag.name.toLowerCase().contains(query));
      final categoryNameMatch =
          contact.contactCategory?.name.toLowerCase().contains(query) ?? false;
      final bucketNameMatch = contact.bucketIds.any((id) {
        final bucketName = contactBuckets
                .firstWhereOrNull((b) => b.id == id)
                ?.name
                .toLowerCase() ??
            '';
        return bucketName.contains(query);
      });

      final searchMatch = query.isEmpty ||
          nameMatch ||
          orgMatch ||
          emailMatch ||
          phoneMatch ||
          tagMatch ||
          categoryNameMatch ||
          bucketNameMatch;

      return categoryMatch && bucketMatch && searchMatch;
    }).toList();

    // 🔑 Ensure results are always alphabetically sorted
    filteredContacts
        .sort((a, b) => a.name.toLowerCase().compareTo(b.name.toLowerCase()));
  }

  void toggleCategory(String categoryId) {
    if (selectedCategories.contains(categoryId)) {
      selectedCategories.remove(categoryId);
    } else {
      selectedCategories.add(categoryId);
    }
  }

  void toggleBucket(String bucketId) {
    if (selectedBuckets.contains(bucketId)) {
      selectedBuckets.remove(bucketId);
    } else {
      selectedBuckets.add(bucketId);
    }
  }

  void updateSearchQuery(String value) {
    searchQuery.value = value;
    applyFilters();
  }

  String getBucketNames(ContactModel contact, List<ContactBucket> allBuckets) {
    return contact.bucketIds
        .map((id) => allBuckets.firstWhereOrNull((b) => b.id == id)?.name ?? '')
        .where((name) => name.isNotEmpty)
        .join(', ');
  }

  bool hasActiveFilters() {
    return selectedCategories.isNotEmpty ||
        selectedBuckets.isNotEmpty ||
        searchQuery.value.trim().isNotEmpty;
  }
}
