import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';

class TagHelper {
  static Map<String, dynamic> updateTags<T>({
    required List<T> updatedTags,
    required List<dynamic>? subspaces,
    required String Function(T) getInternalId,
    required String? Function(T) getLocation,
    required void Function(T, String) setLocation,
    required String Function(dynamic) getSubspaceName,
    required List<T>? Function(dynamic) getSubspaceTags,
    required void Function(dynamic, List<T>?) setSubspaceTags,
    required int? Function(T, List<dynamic>) checkTagExistInSubspace,
  }) {
    final modifiedTags = List<T>.from(updatedTags);
    final modifiedSubspaces = List<dynamic>.from(subspaces ?? []);

    if (subspaces != null) {
      for (var subspace in subspaces) {
        getSubspaceTags(subspace)?.removeWhere(
          (tag) => !modifiedTags.any(
              (updatedTag) => getInternalId(updatedTag) == getInternalId(tag)),
        );
      }
    }

    for (var tag in modifiedTags.toList()) {
      if (modifiedSubspaces.isEmpty) continue;

      final prevIndice = checkTagExistInSubspace(tag, modifiedSubspaces);
      final tagLocation = getLocation(tag);

      if ((tagLocation == 'Main Space' || tagLocation == null) &&
          (prevIndice == null ||
              getSubspaceName(modifiedSubspaces[prevIndice]) == 'Main Space')) {
        continue;
      }

      if ((tagLocation == 'Main Space' || tagLocation == null) &&
          prevIndice != null) {
        getSubspaceTags(modifiedSubspaces[prevIndice])
            ?.removeWhere((t) => getInternalId(t) == getInternalId(tag));
        continue;
      }

      if ((tagLocation != 'Main Space' && tagLocation != null) &&
          prevIndice == null) {
        final newIndex = modifiedSubspaces
            .indexWhere((subspace) => getSubspaceName(subspace) == tagLocation);

        if (newIndex != -1) {
          if (getSubspaceTags(modifiedSubspaces[newIndex])
                  ?.any((t) => getInternalId(t) == getInternalId(tag)) !=
              true) {
            setLocation(tag, getSubspaceName(modifiedSubspaces[newIndex]));
            final subspaceTags =
                getSubspaceTags(modifiedSubspaces[newIndex]) ?? [];
            subspaceTags.add(tag);
            setSubspaceTags(modifiedSubspaces[newIndex], subspaceTags);
          }
        }

        modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
        continue;
      }

      if ((tagLocation != 'Main Space' && tagLocation != null) &&
          tagLocation != getSubspaceName(modifiedSubspaces[prevIndice!])) {
        getSubspaceTags(modifiedSubspaces[prevIndice])
            ?.removeWhere((t) => getInternalId(t) == getInternalId(tag));

        final newIndex = modifiedSubspaces
            .indexWhere((subspace) => getSubspaceName(subspace) == tagLocation);

        if (newIndex != -1) {
          if (getSubspaceTags(modifiedSubspaces[newIndex])
                  ?.any((t) => getInternalId(t) == getInternalId(tag)) !=
              true) {
            setLocation(tag, getSubspaceName(modifiedSubspaces[newIndex]));
            final subspaceTags =
                getSubspaceTags(modifiedSubspaces[newIndex]) ?? [];
            subspaceTags.add(tag);
            setSubspaceTags(modifiedSubspaces[newIndex], subspaceTags);
          }
        }

        modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
        continue;
      }

      if ((tagLocation != 'Main Space' && tagLocation != null) &&
          tagLocation == getSubspaceName(modifiedSubspaces[prevIndice!])) {
        modifiedTags.removeWhere((t) => getInternalId(t) == getInternalId(tag));
        continue;
      }

      if ((tagLocation == 'Main Space' || tagLocation == null) &&
          prevIndice != null) {
        getSubspaceTags(modifiedSubspaces[prevIndice])
            ?.removeWhere((t) => getInternalId(t) == getInternalId(tag));
      }
    }

    return {
      'updatedTags': modifiedTags,
      'subspaces': modifiedSubspaces,
    };
  }

  static List<String> getAvailableTags<T>({
    required List<String> allTags,
    required List<T> currentTags,
    required T currentTag,
    required String? Function(T) getTag, // Allow nullable return type
  }) {
    return allTags
        .where((tagValue) => !currentTags
            .where((e) => e != currentTag) // Exclude the current row
            .map((e) => getTag(e) ?? '') // Handle null values gracefully
            .contains(tagValue))
        .toList();
  }

  static List<String> getAvailableTagModels(
      List<String> allTags, List<Tag> currentTags, Tag currentTag) {
    List<String> availableTagsForTagModel =
        TagHelper.getAvailableTags<Tag>(
      allTags: allTags,
      currentTags: currentTags,
      currentTag: currentTag,
      getTag: (tag) => tag.tag ?? '',
    );
    return availableTagsForTagModel;
  }

  static List<Tag> generateInitialTags({
    List<Tag>? spaceTagModels,
    List<SubspaceTemplateModel>? subspaces,
  }) {
    final List<Tag> initialTags = [];

    if (spaceTagModels != null) {
      initialTags.addAll(spaceTagModels);
    }

    if (subspaces != null) {
      for (var subspace in subspaces) {
        if (subspace.tags != null) {
          initialTags.addAll(
            subspace.tags!.map(
              (tag) => tag.copyWith(
                location: subspace.subspaceName,
                internalId: tag.internalId,
                tag: tag.tag,
              ),
            ),
          );
        }
      }
    }

    return initialTags;
  }

  static List<Tag> generateInitialForTags({
    List<Tag>? spaceTags,
    List<SubspaceModel>? subspaces,
  }) {
    final List<Tag> initialTags = [];

    if (spaceTags != null) {
      initialTags.addAll(spaceTags);
    }

    if (subspaces != null) {
      for (var subspace in subspaces) {
        if (subspace.tags != null) {
          initialTags.addAll(
            subspace.tags!.map(
              (tag) => tag.copyWith(
                location: subspace.subspaceName,
                internalId: tag.internalId,
                tag: tag.tag,
              ),
            ),
          );
        }
      }
    }

    return initialTags;
  }

  static Map<ProductModel, int> groupTags(List<BaseTag> tags) {
    final Map<ProductModel, int> groupedTags = {};
    for (var tag in tags) {
      if (tag.product != null) {
        final product = tag.product!;
        groupedTags[product] = (groupedTags[product] ?? 0) + 1;
      }
    }
    return groupedTags;
  }

  static List<SelectedProduct> createInitialSelectedProducts(
      List<Tag>? tags, List<SubspaceTemplateModel>? subspaces) {
    final Map<ProductModel, int> productCounts = {};

    if (tags != null) {
      for (var tag in tags) {
        if (tag.product != null) {
          productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
        }
      }
    }

    if (subspaces != null) {
      for (var subspace in subspaces) {
        if (subspace.tags != null) {
          for (var tag in subspace.tags!) {
            if (tag.product != null) {
              productCounts[tag.product!] =
                  (productCounts[tag.product!] ?? 0) + 1;
            }
          }
        }
      }
    }

    return productCounts.entries
        .map((entry) => SelectedProduct(
              productId: entry.key.uuid,
              count: entry.value,
              productName: entry.key.name ?? 'Unnamed',
              product: entry.key,
            ))
        .toList();
  }

  static List<SelectedProduct> createInitialSelectedProductsForTags(
      List<Tag>? tags, List<SubspaceModel>? subspaces) {
    final Map<ProductModel, int> productCounts = {};

    if (tags != null) {
      for (var tag in tags) {
        if (tag.product != null) {
          productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
        }
      }
    }

    if (subspaces != null) {
      for (var subspace in subspaces) {
        if (subspace.tags != null) {
          for (var tag in subspace.tags!) {
            if (tag.product != null) {
              productCounts[tag.product!] =
                  (productCounts[tag.product!] ?? 0) + 1;
            }
          }
        }
      }
    }

    return productCounts.entries
        .map((entry) => SelectedProduct(
              productId: entry.key.uuid,
              count: entry.value,
              productName: entry.key.name ?? 'Unnamed',
              product: entry.key,
            ))
        .toList();
  }

  static int? checkTagExistInSubspaceModels(
      Tag tag, List<dynamic>? subspaces) {
    if (subspaces == null) return null;

    for (int i = 0; i < subspaces.length; i++) {
      final subspace = subspaces[i] as SubspaceTemplateModel; // Explicit cast
      if (subspace.tags == null) continue;
      for (var t in subspace.tags!) {
        if (tag.internalId == t.internalId) {
          return i;
        }
      }
    }
    return null;
  }

  static Map<String, dynamic> updateSubspaceTagModels(
      List<Tag> updatedTags, List<SubspaceTemplateModel>? subspaces) {
    final result = TagHelper.updateTags<Tag>(
      updatedTags: updatedTags,
      subspaces: subspaces,
      getInternalId: (tag) => tag.internalId,
      getLocation: (tag) => tag.location,
      setLocation: (tag, location) => tag.location = location,
      getSubspaceName: (subspace) => subspace.subspaceName,
      getSubspaceTags: (subspace) => subspace.tags,
      setSubspaceTags: (subspace, tags) => subspace.tags = tags,
      checkTagExistInSubspace: checkTagExistInSubspaceModels,
    );

    final processedTags = result['updatedTags'] as List<Tag>;
    final processedSubspaces =
        List<SubspaceTemplateModel>.from(result['subspaces'] as List<dynamic>);

    for (var subspace in processedSubspaces) {
      final subspaceTags = subspace.tags;

      if (subspaceTags != null) {
        for (int i = 0; i < subspaceTags.length; i++) {
          final tag = subspaceTags[i];

          // Find the updated tag inside processedTags
          final changedTag = updatedTags.firstWhere(
            (t) => t.internalId == tag.internalId,
            orElse: () => tag,
          );

          if (changedTag.tag != tag.tag) {
            subspaceTags[i] = changedTag.copyWith(tag: changedTag.tag);
          }
        }
      }

      subspace.tags = subspaceTags;
    }

    return {'updatedTags': processedTags, 'subspaces': processedSubspaces};
  }

  static int? checkTagExistInSubspace(Tag tag, List<dynamic>? subspaces) {
    if (subspaces == null) return null;
    for (int i = 0; i < subspaces.length; i++) {
      final subspace = subspaces[i];
      if (subspace.tags == null) continue;
      for (var t in subspace.tags!) {
        if (tag.internalId == t.internalId) {
          return i;
        }
      }
    }
    return null;
  }

  static Map<String, dynamic> processTags(
      List<Tag> updatedTags, List<SubspaceModel>? subspaces) {
    final result = TagHelper.updateTags<Tag>(
      updatedTags: updatedTags,
      subspaces: subspaces,
      getInternalId: (tag) => tag.internalId,
      getLocation: (tag) => tag.location,
      setLocation: (tag, location) => tag.location = location,
      getSubspaceName: (subspace) => subspace.subspaceName,
      getSubspaceTags: (subspace) => subspace.tags,
      setSubspaceTags: (subspace, tags) => subspace.tags = tags,
      checkTagExistInSubspace: checkTagExistInSubspace,
    );

    final processedTags = result['updatedTags'] as List<Tag>;
    final processedSubspaces =
        List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);

    for (var subspace in processedSubspaces) {
      final subspaceTags = subspace.tags;

      if (subspaceTags != null) {
        for (int i = 0; i < subspaceTags.length; i++) {
          final tag = subspaceTags[i];

          final changedTag = updatedTags.firstWhere(
            (t) => t.internalId == tag.internalId,
            orElse: () => tag,
          );

          if (changedTag.tag != tag.tag) {
            subspaceTags[i] = changedTag.copyWith(tag: changedTag.tag);
          }
        }
      }

      subspace.tags = subspaceTags;
    }

    return {'updatedTags': processedTags, 'subspaces': processedSubspaces};
  }

  static List<String> getAllTagValues(
      List<CommunityModel> communities, List<SpaceTemplateModel>? spaceModels) {
    final Set<String> allTags = {};

    if (spaceModels != null) {
      for (var model in spaceModels) {
        allTags.addAll(model.listAllTagValues());
      }
    }

    for (final community in communities) {
      for (final space in community.spaces) {
        if (space.tags != null) {
          allTags.addAll(space.listAllTagValues());
        }
      }
    }

    return allTags.toList();
  }
}
