import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart';
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.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';
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
import 'package:syncrow_web/services/space_model_mang_api.dart';
import 'package:syncrow_web/utils/constants/action_enum.dart';

class CreateSpaceModelBloc
    extends Bloc<CreateSpaceModelEvent, CreateSpaceModelState> {
  SpaceTemplateModel? _space;

  final SpaceModelManagementApi _api;

  CreateSpaceModelBloc(this._api) : super(CreateSpaceModelInitial()) {
    on<CreateSpaceTemplate>((event, emit) async {
      try {
        final projectUuid = await ProjectManager.getProjectUUID() ?? '';

        late SpaceTemplateModel spaceTemplate = event.spaceTemplate;

        final tagBodyModels =
            spaceTemplate.tags?.map((tag) => tag.toTagBodyModel()).toList() ??
                [];

        final subspaceTemplateBodyModels =
            spaceTemplate.subspaceModels?.map((subspaceModel) {
                  final tagsubspaceBodyModels = subspaceModel.tags
                          ?.map((tag) => tag.toTagBodyModel())
                          .toList() ??
                      [];
                  return CreateSubspaceTemplateModel()
                    ..subspaceName = subspaceModel.subspaceName
                    ..tags = tagsubspaceBodyModels;
                }).toList() ??
                [];
        final spaceModelBody = CreateSpaceTemplateBodyModel(
            modelName: spaceTemplate.modelName,
            tags: tagBodyModels,
            subspaceModels: subspaceTemplateBodyModels);

        final newSpaceTemplate =
            await _api.createSpaceModel(spaceModelBody, projectUuid);
        spaceTemplate.uuid = newSpaceTemplate?.uuid ?? '';

        if (newSpaceTemplate != null) {
          emit(CreateSpaceModelLoaded(spaceTemplate));

          if (event.onCreate != null) {
            event.onCreate!(spaceTemplate);
          }
        }
      } catch (e) {
        emit(CreateSpaceModelError('Error creating space model'));
      }
    });

    on<LoadSpaceTemplate>((event, emit) {
      emit(CreateSpaceModelLoading());
      Future.delayed(const Duration(seconds: 1), () {
        if (_space != null) {
          emit(CreateSpaceModelLoaded(_space!));
        } else {
          emit(CreateSpaceModelError("No space template found"));
        }
      });
    });

    on<UpdateSpaceTemplate>((event, emit) {
      _space = event.spaceTemplate;
      final String? errorMessage = _checkDuplicateModelName(
          event.allModels ?? [], event.spaceTemplate.modelName);
      emit(CreateSpaceModelLoaded(_space!, errorMessage: errorMessage));
    });

    on<AddSubspacesToSpaceTemplate>((event, emit) {
      final currentState = state;

      if (currentState is CreateSpaceModelLoaded) {
        final eventSubspaceIds =
            event.subspaces.map((e) => e.internalId).toSet();

        // Update or retain subspaces
        final updatedSubspaces = currentState.space.subspaceModels
                ?.where((subspace) =>
                    eventSubspaceIds.contains(subspace.internalId))
                .map((subspace) {
              final matchingEventSubspace = event.subspaces.firstWhere(
                (e) => e.internalId == subspace.internalId,
                orElse: () => subspace,
              );


              final updatedTags = [
                ...?subspace.tags?.map<Tag>((tag) {
                      final matchingTag =
                          matchingEventSubspace.tags?.firstWhere(
                        (e) => e.internalId == tag.internalId,
                        orElse: () => tag,
                      );
                      final isUpdated = matchingTag != tag;
                      return isUpdated
                          ? tag.copyWith(tag: matchingTag?.tag)
                          : tag;
                    }) ??
                    <Tag>[],
                ...?matchingEventSubspace.tags?.where(
                      (e) =>
                          subspace.tags
                              ?.every((t) => t.internalId != e.internalId) ??
                          true,
                    ) ??
                    <Tag>[],
              ];
              return subspace.copyWith(
                subspaceName: matchingEventSubspace.subspaceName,
                tags: updatedTags,
              );
            }).toList() ??
            [];

        // Add new subspaces
        event.subspaces
            .where((e) =>
                updatedSubspaces.every((s) => s.internalId != e.internalId))
            .forEach((newSubspace) {
          updatedSubspaces.add(newSubspace);
        });

        final updatedSpace =
            currentState.space.copyWith(subspaceModels: updatedSubspaces);

        emit(CreateSpaceModelLoaded(updatedSpace,
            errorMessage: currentState.errorMessage));
      } else {
        emit(CreateSpaceModelError("Space template not initialized"));
      }
    });

    on<AddTagsToSpaceTemplate>((event, emit) {
      final currentState = state;

      if (currentState is CreateSpaceModelLoaded) {
        final eventTagIds = event.tags.map((e) => e.internalId).toSet();

        final updatedTags = currentState.space.tags
                ?.where((tag) => eventTagIds.contains(tag.internalId))
                .map((tag) {
              final matchingEventTag = event.tags.firstWhere(
                (e) => e.internalId == tag.internalId,
                orElse: () => tag,
              );
              return matchingEventTag != tag
                  ? tag.copyWith(tag: matchingEventTag.tag)
                  : tag;
            }).toList() ??
            [];

        event.tags
            .where(
                (e) => updatedTags.every((t) => t.internalId != e.internalId))
            .forEach((e) {
          updatedTags.add(e);
        });

        emit(CreateSpaceModelLoaded(
            currentState.space.copyWith(tags: updatedTags)));
      } else {
        emit(CreateSpaceModelError("Space template not initialized"));
      }
    });

    on<UpdateSpaceTemplateName>((event, emit) {
      final currentState = state;
      if (currentState is CreateSpaceModelLoaded) {
        if (event.allModels.contains(event.name) == true) {
          emit(CreateSpaceModelLoaded(
            currentState.space,
            errorMessage: "Duplicate Model name",
          ));
        } else if (event.name.trim().isEmpty) {
          emit(CreateSpaceModelLoaded(
            currentState.space,
            errorMessage: "Model name cannot be empty",
          ));
        } else {
          final updatedSpaceModel =
              currentState.space.copyWith(modelName: event.name);

          emit(CreateSpaceModelLoaded(updatedSpaceModel));
        }
      } else {
        emit(CreateSpaceModelError("Space template not initialized"));
      }
    });

    on<ModifySpaceTemplate>((event, emit) async {
      try {
        final projectUuid = await ProjectManager.getProjectUUID() ?? '';

        if (event.spaceTemplate.uuid != null) {
          final prevSpaceModel = await _api.getSpaceModel(
              event.spaceTemplate.uuid ?? '', projectUuid);

          final newSpaceModel = event.updatedSpaceTemplate;
          String? spaceModelName;
          if (prevSpaceModel?.modelName != newSpaceModel.modelName) {
            spaceModelName = newSpaceModel.modelName;
          }
          List<TagModelUpdate> tagUpdates = [];
          final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
          final List<SubspaceTemplateModel>? prevSubspaces =
              prevSpaceModel?.subspaceModels;
          final List<SubspaceTemplateModel>? newSubspaces =
              newSpaceModel.subspaceModels;

          tagUpdates =
              processTagUpdates(prevSpaceModel?.tags, newSpaceModel.tags);

          if (prevSubspaces != null || newSubspaces != null) {
            if (prevSubspaces != null && newSubspaces != null) {
              for (var prevSubspace in prevSubspaces) {
                final existsInNew = newSubspaces
                    .any((subspace) => subspace.uuid == prevSubspace.uuid);
                if (!existsInNew) {
                  subspaceUpdates.add(UpdateSubspaceTemplateModel(
                      action: Action.delete, uuid: prevSubspace.uuid));
                }
              }
            } else if (prevSubspaces != null && newSubspaces == null) {
              for (var prevSubspace in prevSubspaces) {
                subspaceUpdates.add(UpdateSubspaceTemplateModel(
                    action: Action.delete, uuid: prevSubspace.uuid));
              }
            }

            if (newSubspaces != null) {
              for (var newSubspace in newSubspaces) {
                // Tag without UUID
                if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
                  final List<TagModelUpdate> tagUpdates = [];

                  if (newSubspace.tags != null) {
                    for (var tag in newSubspace.tags!) {
                      tagUpdates.add(TagModelUpdate(
                          action: Action.add,
                          newTagUuid: tag.uuid == '' ? null : tag.uuid,
                          tag: tag.tag,
                          productUuid: tag.product?.uuid));
                    }
                  }
                  subspaceUpdates.add(UpdateSubspaceTemplateModel(
                      action: Action.add,
                      subspaceName: newSubspace.subspaceName,
                      tags: tagUpdates));
                }
              }
            }

            if (prevSubspaces != null && newSubspaces != null) {
              final newSubspaceMap = {
                for (var subspace in newSubspaces) subspace.uuid: subspace
              };

              for (var prevSubspace in prevSubspaces) {
                final newSubspace = newSubspaceMap[prevSubspace.uuid];

                if (newSubspace != null) {
                  final List<TagModelUpdate> tagSubspaceUpdates =
                      processTagUpdates(prevSubspace.tags, newSubspace.tags);
                  subspaceUpdates.add(UpdateSubspaceTemplateModel(
                      action: Action.update,
                      uuid: newSubspace.uuid,
                      subspaceName: newSubspace.subspaceName,
                      tags: tagSubspaceUpdates));
                }
              }
            }
          }

          final spaceModelBody = CreateSpaceTemplateBodyModel(
              modelName: spaceModelName,
              tags: tagUpdates,
              subspaceModels: subspaceUpdates);

          final res = await _api.updateSpaceModel(
              spaceModelBody, prevSpaceModel?.uuid ?? '', projectUuid);

          if (res != null) {
            emit(CreateSpaceModelLoaded(newSpaceModel));
            if (event.onUpdate != null) {
              event.onUpdate!(event.updatedSpaceTemplate);
            }
          }
        }
      } catch (e) {
        emit(CreateSpaceModelError('Error creating space model'));
      }
    });
  }

  List<TagModelUpdate> processTagUpdates(
    List<Tag>? prevTags,
    List<Tag>? newTags,
  ) {
    final List<TagModelUpdate> tagUpdates = [];
    final processedTags = <String?>{};

    if (prevTags == null && newTags != null) {
      for (var newTag in newTags) {
        tagUpdates.add(TagModelUpdate(
          action: Action.add,
          tag: newTag.tag,
          newTagUuid: newTag.uuid,
          productUuid: newTag.product?.uuid,
        ));
      }
      return tagUpdates;
    }

    if (newTags != null || prevTags != null) {
      // Case 1: Tags deleted
      if (prevTags != null && newTags != null) {
        for (var prevTag in prevTags) {
          final existsInNew =
              newTags.any((newTag) => newTag.uuid == prevTag.uuid);
          if (!existsInNew) {
            tagUpdates
                .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
          }
        }
      } else if (prevTags != null && newTags == null) {
        for (var prevTag in prevTags) {
          tagUpdates
              .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
        }
      }

      // Case 2: Tags added
      if (newTags != null) {
        final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};

        for (var newTag in newTags) {
          // Tag without UUID
          if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
              !processedTags.contains(newTag.tag)) {
            tagUpdates.add(TagModelUpdate(
                action: Action.add,
                tag: newTag.tag,
                newTagUuid: newTag.uuid == '' ? null : newTag.uuid,
                productUuid: newTag.product?.uuid));
            processedTags.add(newTag.tag);
          }
        }
      }

      // Case 3: Tags updated
      if (prevTags != null && newTags != null) {
        final newTagMap = {for (var tag in newTags) tag.uuid: tag};

        for (var prevTag in prevTags) {
          final newTag = newTagMap[prevTag.uuid];
          if (newTag != null) {
            tagUpdates.add(TagModelUpdate(
              action: Action.update,
              uuid: prevTag.uuid,
              newTagUuid: newTag.uuid,
              tag: newTag.tag,
            ));
          } else {}
        }
      }
    }

    return tagUpdates;
  }

  String? _checkDuplicateModelName(List<String> allModels, String name) {
    if (allModels.contains(name)) {
      return "Duplicate Model name";
    }
    return null;
  }
}
