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

class DirectoryController extends GetxController {
  // -------------------- CONTACTS --------------------
  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;

  // -------------------- COMMENTS --------------------
  final Map<String, RxList<DirectoryComment>> activeCommentsMap = {};
  final Map<String, RxList<DirectoryComment>> inactiveCommentsMap = {};
  final editingCommentId = Rxn<String>();

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

  // -------------------- COMMENTS HANDLING --------------------

  RxList<DirectoryComment> getCommentsForContact(String contactId,
      {bool active = true}) {
    return active
        ? activeCommentsMap[contactId] ?? <DirectoryComment>[].obs
        : inactiveCommentsMap[contactId] ?? <DirectoryComment>[].obs;
  }

  Future<void> fetchCommentsForContact(String contactId,
      {bool active = true}) async {
    try {
      final data =
          await ApiService.getDirectoryComments(contactId, active: active);
      var comments =
          data?.map((e) => DirectoryComment.fromJson(e)).toList() ?? [];

      // ✅ Deduplicate by ID before storing
      final Map<String, DirectoryComment> uniqueMap = {
        for (var c in comments) c.id: c,
      };
      comments = uniqueMap.values.toList()
        ..sort((a, b) => b.createdAt.compareTo(a.createdAt));

      if (active) {
        activeCommentsMap[contactId] = <DirectoryComment>[].obs
          ..assignAll(comments);
      } else {
        inactiveCommentsMap[contactId] = <DirectoryComment>[].obs
          ..assignAll(comments);
      }
    } catch (e, stack) {
      logSafe("Error fetching ${active ? 'active' : 'inactive'} comments: $e",
          level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);

      if (active) {
        activeCommentsMap[contactId] = <DirectoryComment>[].obs;
      } else {
        inactiveCommentsMap[contactId] = <DirectoryComment>[].obs;
      }
    }
  }

  List<DirectoryComment> combinedComments(String contactId) {
    final activeList = getCommentsForContact(contactId, active: true);
    final inactiveList = getCommentsForContact(contactId, active: false);

    // ✅ Deduplicate by ID (active wins)
    final Map<String, DirectoryComment> byId = {};
    for (final c in inactiveList) {
      byId[c.id] = c;
    }
    for (final c in activeList) {
      byId[c.id] = c;
    }

    final combined = byId.values.toList()
      ..sort((a, b) => b.createdAt.compareTo(a.createdAt));
    return combined;
  }

  Future<void> updateComment(DirectoryComment comment) async {
    try {
      final existing = getCommentsForContact(comment.contactId)
          .firstWhereOrNull((c) => c.id == comment.id);

      if (existing != null && existing.note.trim() == comment.note.trim()) {
        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) {
        await fetchCommentsForContact(comment.contactId, active: true);
        await fetchCommentsForContact(comment.contactId, active: false);
        showAppSnackbar(
          title: "Success",
          message: "Comment updated successfully.",
          type: SnackbarType.success,
        );
      } else {
        showAppSnackbar(
          title: "Error",
          message: "Failed to update comment.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Update comment failed: $e", level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Failed to update comment.",
        type: SnackbarType.error,
      );
    }
  }

  Future<void> deleteComment(String commentId, String contactId) async {
    try {
      final success = await ApiService.restoreContactComment(commentId, false);

      if (success) {
        if (editingCommentId.value == commentId) editingCommentId.value = null;
        await fetchCommentsForContact(contactId, active: true);
        await fetchCommentsForContact(contactId, active: false);
        showAppSnackbar(
          title: "Deleted",
          message: "Comment deleted successfully.",
          type: SnackbarType.success,
        );
      } else {
        showAppSnackbar(
          title: "Error",
          message: "Failed to delete comment.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Delete comment failed: $e", level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Something went wrong while deleting comment.",
        type: SnackbarType.error,
      );
    }
  }

  Future<void> restoreComment(String commentId, String contactId) async {
    try {
      final success = await ApiService.restoreContactComment(commentId, true);

      if (success) {
        await fetchCommentsForContact(contactId, active: true);
        await fetchCommentsForContact(contactId, active: false);
        showAppSnackbar(
          title: "Restored",
          message: "Comment restored successfully.",
          type: SnackbarType.success,
        );
      } else {
        showAppSnackbar(
          title: "Error",
          message: "Failed to restore comment.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Restore comment failed: $e", level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Something went wrong while restoring comment.",
        type: SnackbarType.error,
      );
    }
  }

  // -------------------- CONTACTS HANDLING --------------------

  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);
    }
  }
// -------------------- CONTACT DELETION / RESTORE --------------------

  Future<void> deleteContact(String contactId) async {
    try {
      final success = await ApiService.deleteDirectoryContact(contactId);
      if (success) {
        // Refresh contacts after deletion
        await fetchContacts(active: true);
        await fetchContacts(active: false);
        showAppSnackbar(
          title: "Deleted",
          message: "Contact deleted successfully.",
          type: SnackbarType.success,
        );
      } else {
        showAppSnackbar(
          title: "Error",
          message: "Failed to delete contact.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Delete contact failed: $e", level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Something went wrong while deleting contact.",
        type: SnackbarType.error,
      );
    }
  }

  Future<void> restoreContact(String contactId) async {
    try {
      final success = await ApiService.restoreDirectoryContact(contactId);
      if (success) {
        // Refresh contacts after restore
        await fetchContacts(active: true);
        await fetchContacts(active: false);
        showAppSnackbar(
          title: "Restored",
          message: "Contact restored successfully.",
          type: SnackbarType.success,
        );
      } else {
        showAppSnackbar(
          title: "Error",
          message: "Failed to restore contact.",
          type: SnackbarType.error,
        );
      }
    } catch (e, stack) {
      logSafe("Restore contact failed: $e", level: LogLevel.error);
      logSafe(stack.toString(), level: LogLevel.debug);
      showAppSnackbar(
        title: "Error",
        message: "Something went wrong while restoring contact.",
        type: SnackbarType.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.putIfAbsent(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();

    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;
  }
}
