package cn.novalon.gym.manage.member.util;

import cn.hutool.core.bean.BeanUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

/**
 * 通用 ES 同步工具类
 *
 * 使用方式：
 *
 * 1. 注入工具类
 *    @Autowired private EsSyncUtils esSyncUtils;
 *
 * 2. 同步数据到 ES（不返回结果，适合 doOnSuccess）
 *    esSyncUtils.sync(Member.class, MemberES.class, member, memberESRepository);
 *
 * 3. 同步数据到 ES（返回 Mono，适合链式调用）
 *    esSyncUtils.syncToES(Member.class, MemberES.class, member, memberESRepository).subscribe();
 *
 * 4. 如果 Repository 是单例，可以先绑定
 *    var syncer = esSyncUtils.bind(Member.class, MemberES.class, memberESRepository);
 *    syncer.sync(member);
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class EsSyncUtils {

    /**
     * 同步实体到 ES（不返回结果，适合 doOnSuccess）
     *
     * @param sourceClass 源实体类（如 Member.class）
     * @param targetClass 目标ES实体类（如 MemberES.class）
     * @param source 源实体对象
     * @param repository ES Repository
     * @param <S> 源实体类型
     * @param <T> ES实体类型
     * @param <ID> ID类型
     */
    public <S, T, ID> void sync(Class<S> sourceClass, Class<T> targetClass,
                                S source, ReactiveElasticsearchRepository<T, ID> repository) {
        if (source == null) {
            log.warn("同步 ES 失败：源实体为空");
            return;
        }

        try {
            T target = BeanUtil.toBean(source, targetClass);
            repository.save(target).subscribe(
                    success -> log.debug("同步到 ES 成功, 类型: {}", targetClass.getSimpleName()),
                    error -> log.error("同步到 ES 失败, 类型: {}", targetClass.getSimpleName(), error)
            );
        } catch (Exception e) {
            log.error("转换 ES 实体失败, 源类型: {}, 目标类型: {}",
                    sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
        }
    }

    /**
     * 同步实体到 ES（返回 Mono，适合链式调用）
     *
     * @param sourceClass 源实体类
     * @param targetClass 目标ES实体类
     * @param source 源实体对象
     * @param repository ES Repository
     * @return Mono<Void>
     */
    public <S, T, ID> Mono<Void> syncToES(Class<S> sourceClass, Class<T> targetClass,
                                          S source, ReactiveElasticsearchRepository<T, ID> repository) {
        if (source == null) {
            log.warn("同步 ES 失败：源实体为空");
            return Mono.empty();
        }

        try {
            T target = BeanUtil.toBean(source, targetClass);
            return repository.save(target)
                    .doOnSuccess(t -> log.debug("同步到 ES 成功, 类型: {}", targetClass.getSimpleName()))
                    .doOnError(e -> log.error("同步到 ES 失败, 类型: {}", targetClass.getSimpleName(), e))
                    .then();
        } catch (Exception e) {
            log.error("转换 ES 实体失败, 源类型: {}, 目标类型: {}",
                    sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
            return Mono.empty();
        }
    }

    /**
     * 绑定 Repository，返回一个针对特定实体类型的同步器
     *
     * @param sourceClass 源实体类
     * @param targetClass 目标ES实体类
     * @param repository ES Repository
     * @return 实体同步器
     */
    public <S, T, ID> EntitySyncer<S, T, ID> bind(Class<S> sourceClass, Class<T> targetClass,
                                                  ReactiveElasticsearchRepository<T, ID> repository) {
        return new EntitySyncer<>(sourceClass, targetClass, repository);
    }

    /**
     * 实体同步器（绑定特定类型的同步器，避免重复传 Class）
     *
     * @param <S> 源实体类型
     * @param <T> ES实体类型
     * @param <ID> ID类型
     */
    public static class EntitySyncer<S, T, ID> {
        private final Class<S> sourceClass;
        private final Class<T> targetClass;
        private final ReactiveElasticsearchRepository<T, ID> repository;

        public EntitySyncer(Class<S> sourceClass, Class<T> targetClass,
                            ReactiveElasticsearchRepository<T, ID> repository) {
            this.sourceClass = sourceClass;
            this.targetClass = targetClass;
            this.repository = repository;
        }

        /**
         * 同步（不返回结果）
         */
        public void sync(S source) {
            if (source == null) return;
            try {
                T target = BeanUtil.toBean(source, targetClass);
                repository.save(target).subscribe(
                        success -> log.debug("同步到 ES 成功, 类型: {}", targetClass.getSimpleName()),
                        error -> log.error("同步到 ES 失败, 类型: {}", targetClass.getSimpleName(), error)
                );
            } catch (Exception e) {
                log.error("转换 ES 实体失败, 源类型: {}, 目标类型: {}",
                        sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
            }
        }

        /**
         * 同步（返回 Mono）
         */
        public Mono<Void> syncMono(S source) {
            if (source == null) return Mono.empty();
            try {
                T target = BeanUtil.toBean(source, targetClass);
                return repository.save(target)
                        .doOnSuccess(t -> log.debug("同步到 ES 成功, 类型: {}", targetClass.getSimpleName()))
                        .doOnError(e -> log.error("同步到 ES 失败, 类型: {}", targetClass.getSimpleName(), e))
                        .then();
            } catch (Exception e) {
                log.error("转换 ES 实体失败, 源类型: {}, 目标类型: {}",
                        sourceClass.getSimpleName(), targetClass.getSimpleName(), e);
                return Mono.empty();
            }
        }
    }
}