package top.r3944realms.lib39.core.sync;

import com.google.common.collect.Sets;
import net.minecraft.resources.Identifier;
import net.minecraft.world.entity.Entity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * The type Sync data 2 manager.
 *
 * @param <V> the type parameter
 */
@SuppressWarnings({"unused", "DuplicatedCode"})
public abstract class SyncData2Manager<V extends SyncData2Manager.TypedSyncEntry<?, ?>> {
    /**
     * Gets typed entries.
     *
     * @return the typed entries
     */
    protected abstract Map<Identifier, V> getTypedEntries();

    /**
     * 数据提供者接口 - 用于通过键获取数据
     *
     * @param <K> the type parameter
     * @param <T> the type parameter
     */
    @FunctionalInterface
    public interface DataProvider<K, T> {
        /**
         * 通过键获取数据的 Optional
         *
         * @param key 键
         * @return 数据的 Optional
         */
        Optional<T> getData(K key);
    }

    /**
     * The type Typed sync entry.
     *
     * @param <K> the type parameter
     * @param <T> the type parameter
     */
    protected static class TypedSyncEntry<K, T extends ISyncData<?>> {
        /**
         * The Manager.
         */
        final ISyncManager<K, T> manager;
        /**
         * The Data provider.
         */
        @Nullable
        final DataProvider<Entity, T> dataProvider;
        /**
         * The Allowed classes.
         */
        final Set<Class<?>> allowedClasses;

        /**
         * Instantiates a new Typed sync entry.
         *
         * @param manager      the manager
         * @param dataProvider the data provider
         */
        public TypedSyncEntry(ISyncManager<K, T> manager, @Nullable DataProvider<Entity, T> dataProvider) {
            this.manager = manager;
            this.dataProvider = dataProvider;
            this.allowedClasses = Sets.newConcurrentHashSet();
        }
    }

    /**
     * Register manager with data provider.
     *
     * @param <K>          the type parameter
     * @param <T>          the type parameter
     * @param key          the key
     * @param manager      the manager
     * @param dataProvider the data provider
     */
    @SuppressWarnings("unchecked")
    public <K, T extends ISyncData<?>> void registerManagerWithProvider(
            Identifier key,
            ISyncManager<K, T> manager,
            DataProvider<Entity, T> dataProvider
    ) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(manager, "Sync manager cannot be null");
        Objects.requireNonNull(dataProvider, "Data provider cannot be null");

        getTypedEntries().put(key, (V) new TypedSyncEntry<>(manager, dataProvider));
    }

    /**
     * Register manager with function getter.
     *
     * @param <K>     the type parameter
     * @param <T>     the type parameter
     * @param key     the key
     * @param manager the manager
     * @param getter  the data getter function
     */
    @SuppressWarnings("unchecked")
    public <K, T extends ISyncData<?>> void registerManager(
            Identifier key,
            ISyncManager<K, T> manager,
            Function<Entity, Optional<T>> getter
    ) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(manager, "Sync manager cannot be null");
        Objects.requireNonNull(getter, "Data getter function cannot be null");

        getTypedEntries().put(key, (V) new TypedSyncEntry<>(manager, getter::apply));
    }

    /**
     * 向后兼容的注册方法（只注册管理器，不注册数据提供者）
     *
     * @param key     the key
     * @param manager the manager
     */
    @SuppressWarnings("unchecked")
    public void registerManager(Identifier key, ISyncManager<?, ? extends ISyncData<?>> manager) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(manager, "Sync manager cannot be null");

        // 创建一个没有数据提供者的 TypedSyncEntry
        getTypedEntries().put(key, (V) new TypedSyncEntry<>(
                (ISyncManager<?, ISyncData<?>>) manager,
                null
        ));
    }

    /**
     * Gets manager.
     *
     * @param <K> the type parameter
     * @param <T> the type parameter
     * @param key the key
     * @return the manager
     */
    @SuppressWarnings("unchecked")
    public <K, T extends ISyncData<?>> Optional<ISyncManager<K, T>> getManager(Identifier key) {
        TypedSyncEntry<?,?> entry = getTypedEntries().get(key);
        return entry != null ? Optional.of((ISyncManager<K,T>) entry.manager) : Optional.empty();
    }

    /**
     * Gets data provider.
     *
     * @param <T> the type parameter
     * @param key the key
     * @return the data provider
     */
    @SuppressWarnings("unchecked")
    public <T extends ISyncData<?>> Optional<DataProvider<Entity, T>> getDataProvider(Identifier key) {
        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null && entry.dataProvider != null) {
            return Optional.of((DataProvider<Entity, T>) entry.dataProvider);
        }
        return Optional.empty();
    }

    /**
     * 获取实体数据
     *
     * @param <T>    the type parameter
     * @param key    the key
     * @param entity the entity
     * @return the entity data
     */
    @SuppressWarnings("unchecked")
    public <T extends ISyncData<?>> Optional<T> getEntityData(Identifier key, Entity entity) {
        return getDataProvider(key)
                .flatMap(provider -> {
                    Optional<ISyncData<?>> result = provider.getData(entity);
                    return (Optional<T>) result;
                });
    }

    /**
     * Allow entity class.
     *
     * @param key     the key
     * @param classes the classes
     */
    public final void allowEntityClass(Identifier key, Class<?>... classes) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(classes, "Classes array cannot be null");

        if (classes.length == 0) {
            return;
        }

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null) {
            entry.allowedClasses.addAll(Arrays.asList(classes));
        }
    }

    /**
     * 移除允许的实体类
     *
     * @param key     the key
     * @param classes the classes
     */
    public final void disallowEntityClass(Identifier key, Class<?>... classes) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(classes, "Classes array cannot be null");

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null && classes.length > 0) {
            Arrays.asList(classes).forEach(entry.allowedClasses::remove);
        }
    }

    /**
     * 绑定数据提供者（用于分离注册的情况）
     *
     * @param <T>          the type parameter
     * @param key          the key
     * @param dataProvider the data provider
     */
    public <T extends ISyncData<?>> void bindDataProvider(Identifier key, DataProvider<Entity, T> dataProvider) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(dataProvider, "Data provider cannot be null");

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null) {
            // 更新现有条目的数据提供者
            updateDataProviderInEntry(key, entry, dataProvider);
        } else {
            throw new IllegalArgumentException("No manager found for " + key);
        }
    }

    /**
     * 绑定简单的数据获取器
     *
     * @param <T>    the type parameter
     * @param key    the key
     * @param getter the data getter function
     */
    public <T extends ISyncData<?>> void bindDataGetter(Identifier key, @NotNull Function<Entity, Optional<T>> getter) {
        bindDataProvider(key, getter::apply);
    }

    /**
     * 解绑数据提供者
     *
     * @param key the key
     */
    public void unbindDataProvider(Identifier key) {
        Objects.requireNonNull(key, "Identifier key cannot be null");

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null) {
            // 将数据提供者设置为null，但保留管理器和其他配置
            updateDataProviderInEntry(key, entry, null);
        }
    }

    /**
     * 清除允许的实体类
     *
     * @param key the key
     */
    public void clearAllowedEntityClasses(Identifier key) {
        Objects.requireNonNull(key, "Identifier key cannot be null");

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        if (entry != null) {
            entry.allowedClasses.clear();
        }
    }

    /**
     * Is entity class allowed boolean.
     *
     * @param key         the key
     * @param entityClass the entity class
     * @return the boolean
     */
    public boolean isEntityClassAllowed(Identifier key, Class<?> entityClass) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        Objects.requireNonNull(entityClass, "Entity class cannot be null");

        TypedSyncEntry<?, ?> entry = getTypedEntries().get(key);
        boolean isAllowed = false;
        if (entry != null) {
            for (Class<?> allowedClass : entry.allowedClasses) {
                if (allowedClass.isAssignableFrom(entityClass)) {
                    isAllowed = true;
                    break;
                }
            }
        }
        return entry != null && isAllowed ;
    }

    /**
     * Track entity for manager.
     *
     * @param entity    the entity
     * @param managerId the manager id
     */
    @SuppressWarnings("unchecked")
    public void trackEntityForManager(Entity entity, Identifier managerId) {
        TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) getTypedEntries().get(managerId);
        if (entry != null) {
            trackEntityWithTypedEntry(entity, entry);
        }
    }

    private <T extends ISyncData<?>> void trackEntityWithTypedEntry(Entity entity, @NotNull TypedSyncEntry<UUID, T> entry) {
        if (entry.dataProvider != null) {
            entry.dataProvider.getData(entity)
                    .ifPresent(data -> entry.manager.track(entity.getUUID(), data));
        }
    }

    /**
     * Untrack entity for manager.
     *
     * @param entity    the entity
     * @param managerId the manager id
     */
    @SuppressWarnings("unchecked")
    public void untrackEntityForManager(Entity entity, Identifier managerId) {
        TypedSyncEntry<UUID, ?> entry = (TypedSyncEntry<UUID, ?>) getTypedEntries().get(managerId);
        if (entry != null) {
            untrackEntityWithTypedEntry(entity, entry);
        }
    }

    private <T extends ISyncData<?>> void untrackEntityWithTypedEntry(Entity entity, @NotNull TypedSyncEntry<UUID, T> entry) {
        if (entry.dataProvider != null) {
            entry.dataProvider.getData(entity)
                    .ifPresent(data -> entry.manager.untrack(entity.getUUID(), data));
        }
    }

    /**
     * 从所有管理器中移除实体跟踪
     *
     * @param entity the entity
     */
    public void untrackEntityFromAllManagers(Entity entity) {
        for (Identifier id : getRegisteredKeys()) {
            if (isEntityClassAllowed(id, entity.getClass())) {
                untrackEntityForManager(entity, id);
            }
        }
    }

    /**
     * 批量从管理器中移除实体跟踪
     *
     * @param entities  the entities
     * @param managerId the manager id
     */
    public void untrackEntitiesForManager(@NotNull Iterable<Entity> entities, Identifier managerId) {
        for (Entity entity : entities) {
            untrackEntityForManager(entity, managerId);
        }
    }

    /**
     * 从所有管理器中批量移除实体跟踪
     *
     * @param entities the entities
     */
    public void untrackEntitiesFromAllManagers(@NotNull Iterable<Entity> entities) {
        for (Entity entity : entities) {
            untrackEntityFromAllManagers(entity);
        }
    }

    /**
     * 强制清理管理器中的所有跟踪数据
     *
     * @param managerId the manager id
     */
    public void clearAllTrackedData(Identifier managerId) {
        TypedSyncEntry<?, ?> entry = getTypedEntries().get(managerId);
        if (entry != null) {
            clearTrackedDataForEntry(entry);
        }
    }

    private <K, T extends ISyncData<?>> void clearTrackedDataForEntry(@NotNull TypedSyncEntry<K, T> entry) {
        Set<T> syncSet = entry.manager.getSyncSet();
        if (syncSet != null) {
            syncSet.clear();
        }
    }

    /**
     * 清理所有管理器的跟踪数据
     */
    public void clearAllTrackedData() {
        for (Identifier id : getRegisteredKeys()) {
            clearAllTrackedData(id);
        }
    }

    /**
     * Update data provider in entry.
     *
     * @param <K>             the type parameter
     * @param <T>             the type parameter
     * @param id              the id
     * @param entry           the entry
     * @param newDataProvider the new data provider
     */
// 辅助方法：更新条目的数据提供者
    @SuppressWarnings("unchecked")
    protected <K, T extends ISyncData<?>> void updateDataProviderInEntry(
            Identifier id,
            TypedSyncEntry<?,?> entry,
            DataProvider<Entity, T> newDataProvider
    ) {
        // 由于 DataProvider 是 final，我们需要创建一个新的 TypedSyncEntry
        TypedSyncEntry<K, T> newEntry = new TypedSyncEntry<>(
                (ISyncManager<K, T>) entry.manager,
                newDataProvider
        );
        newEntry.allowedClasses.addAll(entry.allowedClasses);

        getTypedEntries().put(id, (V) newEntry);
    }

    /**
     * Gets registered keys.
     *
     * @return the registered keys
     */
    public Set<Identifier> getRegisteredKeys() {
        return Collections.unmodifiableSet(getTypedEntries().keySet());
    }

    /**
     * For each.
     *
     * @param consumer the consumer
     */
    public void forEach(BiConsumer<Identifier, ISyncManager<?,?>> consumer) {
        Objects.requireNonNull(consumer, "Consumer cannot be null");
        getTypedEntries().forEach((key, entry) -> consumer.accept(key, entry.manager));
    }

    /**
     * Gets manager count.
     *
     * @return the manager count
     */
    public int getManagerCount() {
        return getTypedEntries().size();
    }

    /**
     * Clear all.
     */
    public void clearAll() {
        getTypedEntries().clear();
    }

    /**
     * 移除管理器（包括所有相关配置）
     *
     * @param key the key
     */
    public void removeManager(Identifier key) {
        Objects.requireNonNull(key, "Identifier key cannot be null");
        getTypedEntries().remove(key);
    }
}