package cn.novalon.gym.manage.member.service.impl;

import cn.novalon.gym.manage.common.exception.ConflictException;
import cn.novalon.gym.manage.common.exception.ErrorCode;
import cn.novalon.gym.manage.common.exception.NotFoundException;
import cn.novalon.gym.manage.common.exception.SystemException;
import cn.novalon.gym.manage.member.dto.WechatLoginDto;
import cn.novalon.gym.manage.member.entity.Member;
import cn.novalon.gym.manage.member.es.entity.MemberES;
import cn.novalon.gym.manage.member.es.repository.MemberESRepository;
import cn.novalon.gym.manage.member.repository.IMemberRepository;
import cn.novalon.gym.manage.member.service.WechatApiService;
import cn.novalon.gym.manage.member.service.WechatAuthService;
import cn.novalon.gym.manage.member.util.AesUtil;
import cn.novalon.gym.manage.member.util.EsSyncUtils;
import cn.novalon.gym.manage.member.util.MemberNoGenerator;
import cn.novalon.gym.manage.common.util.RedisUtil;
import cn.novalon.gym.manage.member.util.WechatPhoneUtil;
import cn.novalon.gym.manage.member.vo.WechatLoginVO;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * 微信认证服务实现
 *
 * @author 付嘉
 * @date 2026-05-01
 */

@Slf4j
@Service
@RequiredArgsConstructor
public class WechatAuthServiceImpl implements WechatAuthService {

    private final WechatApiService wechatApiService;
    private final IMemberRepository memberRepository;
    private final WechatPhoneUtil wechatPhoneUtil;
    private final MemberESRepository memberESRepository;
    private final EsSyncUtils esSyncUtils;
    private final JwtTokenProvider jwtTokenProvider;
    private final RedisUtil redisUtil;

    private EsSyncUtils.EntitySyncer<Member, MemberES, String> memberSyncer;

    private static final String MEMBER_INFO_CACHE_PREFIX = "member:info:";
    private static final long CACHE_EXPIRE_SECONDS = 300;

    @PostConstruct
    public void init() {
        this.memberSyncer = esSyncUtils.bind(Member.class, MemberES.class, memberESRepository);
    }


    @Override
    public Mono<WechatLoginVO> miniappLogin(WechatLoginDto request) {
        log.info("开始小程序登录");

        return wechatApiService.jsCode2Session(request.getCode())
                .flatMap(sessionData -> {
                    String openid = sessionData.get("openid");
                    String unionId = sessionData.get("unionid");
                    String sessionKey = sessionData.get("session_key");

                    log.info("微信 API 返回: openid={}, unionid={}", openid, unionId);

                    if (unionId != null && !unionId.isEmpty()) {
                        return memberRepository.findByUnionId(unionId)
                                .flatMap(member -> {
                                    log.info("找到会员, memberId: {}", member.getId());

                                    if (member.getMiniappOpenId() == null || member.getMiniappOpenId().isEmpty()) {
                                        log.info("用户已有 UnionID，补充小程序 OpenID, memberId: {}", member.getId());
                                        member.setMiniappOpenId(openid);
                                        member.setLastLoginAt(LocalDateTime.now());

                                        return memberRepository.save(member)
                                                .doOnSuccess(saved -> {
                                                    memberSyncer.sync(saved);
                                                    clearMemberCache(saved.getId());
                                                })
                                                .flatMap(savedMember -> {
                                                    WechatLoginVO response = buildLoginResponse(savedMember, false, sessionKey);
                                                    return Mono.just(response);
                                                });
                                    } else {
                                        log.info("老用户登录，更新最后登录时间, memberId: {}", member.getId());
                                        member.setLastLoginAt(LocalDateTime.now());

                                        return memberRepository.save(member)
                                                .doOnSuccess(saved -> {
                                                    memberSyncer.sync(saved);
                                                    clearMemberCache(saved.getId());
                                                })
                                                .flatMap(savedMember -> {
                                                    WechatLoginVO response = buildLoginResponse(savedMember, false, sessionKey);
                                                    return Mono.just(response);
                                                });
                                    }
                                })
                                .switchIfEmpty(Mono.defer(() -> {
                                    log.info("UnionID 未找到，尝试通过小程序 OpenID 查询, openid: {}", openid);
                                    return memberRepository.findByMiniappOpenId(openid)
                                            .flatMap(member -> {
                                                log.info("找到会员, memberId: {}", member.getId());
                                                member.setUnionId(unionId);
                                                member.setLastLoginAt(LocalDateTime.now());

                                                return memberRepository.save(member)
                                                        .doOnSuccess(saved -> {
                                                            memberSyncer.sync(saved);
                                                            clearMemberCache(saved.getId());
                                                        })
                                                        .flatMap(savedMember -> {
                                                            WechatLoginVO response = buildLoginResponse(savedMember, false, sessionKey);
                                                            return Mono.just(response);
                                                        });
                                            })
                                            .switchIfEmpty(Mono.defer(() -> {
                                                log.info("OpenID 也未找到，创建新会员（无 UnionID）, openid: {}", openid);
                                                return createNewMember(unionId, openid, sessionKey, request.getPhoneCode());
                                            }));
                                }));
                    } else {
                        log.warn("微信 API 未返回 UnionID，尝试通过小程序 OpenID 查询, openid: {}", openid);
                        return memberRepository.findByMiniappOpenId(openid)
                                .flatMap(member -> {
                                    log.info("找到会员, memberId: {}", member.getId());
                                    member.setLastLoginAt(LocalDateTime.now());

                                    return memberRepository.save(member)
                                            .doOnSuccess(saved -> {
                                                memberSyncer.sync(saved);
                                                clearMemberCache(saved.getId());
                                            })
                                            .flatMap(savedMember -> {
                                                WechatLoginVO response = buildLoginResponse(savedMember, false, sessionKey);
                                                return Mono.just(response);
                                            });
                                })
                                .switchIfEmpty(Mono.defer(() -> {
                                    log.info("OpenID也未找到，创建新会员（无UnionID标识信息）");
                                    return createNewMember(unionId, openid, sessionKey, request.getPhoneCode());
                                }));
                    }
                })
                .onErrorResume(e -> {
                    log.error("小程序登录失败", e);
                    if (e instanceof SystemException) {
                        return Mono.error(e);
                    }
                    return Mono.error(new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "登录失败: " + e.getMessage()));
                });
    }

    @Override
    public Mono<Boolean> bindPhone(Long memberId, String code) {
        log.info("开始绑定手机号, memberId: {}", memberId);

        return wechatApiService.getPhoneNumber(code)
                .flatMap(phoneNumber -> {
                    log.info("获取手机号: {}", phoneNumber);

                    String encryptedPhone = encryptPhone(phoneNumber);

                    return memberRepository.findByPhone(encryptedPhone)
                            .flatMap(existingMember -> {
                                if (!existingMember.getId().equals(memberId)) {
                                    log.warn("手机号已被其他会员绑定, currentMemberId={}, existingMemberId={}", memberId, existingMember.getId());
                                    throw new ConflictException(ErrorCode.CONFLICT_DUPLICATE_USER, "手机号已被其他会员绑定");
                                } else {
                                    log.info("更新会员手机号, memberId: {}", memberId);
                                    return updateMemberPhone(memberId, encryptedPhone);
                                }
                            })
                            .switchIfEmpty(Mono.defer(() -> {
                                log.info("该手机号未被使用，直接绑定到当前会员, memberId: {}", memberId);
                                return updateMemberPhone(memberId, encryptedPhone);
                            }));
                })
                .onErrorResume(e -> {
                    log.error("绑定手机号失败", e);
                    if (e instanceof SystemException) {
                        return Mono.error(e);
                    }
                    return Mono.error(new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "绑定失败: " + e.getMessage()));
                });
    }

    private Mono<Boolean> updateMemberPhone(Long memberId, String encryptedPhone) {
        return memberRepository.findById(memberId)
                .flatMap(member -> {
                    member.setPhone(encryptedPhone);
                    member.setLastLoginAt(LocalDateTime.now());
                    return memberRepository.save(member)
                            .doOnSuccess(saved -> {
                                memberSyncer.sync(saved);
                                clearMemberCache(saved.getId());
                            })
                            .map(savedMember -> {
                                log.info("更新会员手机号成功, memberId: {}", savedMember.getId());
                                return true;
                            });
                })
                .switchIfEmpty(Mono.error(() -> {
                    log.error("会员不存在, memberId={}", memberId);
                    throw new NotFoundException(ErrorCode.NOT_FOUND_USER, "会员不存在");
                }));
    }

    private void clearMemberCache(Long memberId) {
        String cacheKey = MEMBER_INFO_CACHE_PREFIX + memberId;
        redisUtil.delete(cacheKey);
        log.debug("清除会员缓存, memberId: {}", memberId);
    }

    private String encryptPhone(String phoneNumber) {
        try {
            String encryptedPhone = AesUtil.encrypt(phoneNumber);

            log.debug("手机号加密成功");
            return encryptedPhone;
        } catch (Exception e) {
            log.error("手机号加密失败", e);
            throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "手机号加密失败 " + e.getMessage());
        }
    }

    public String decryptPhone(String encryptedPhone) {
        try {

            String phoneNumber = AesUtil.decrypt(encryptedPhone);

            log.debug("手机号解密成功");
            return phoneNumber;
        } catch (Exception e) {
            log.error("手机号解密失败", e);
            throw new SystemException(ErrorCode.SYSTEM_INTERNAL_ERROR, "手机号解密失败 " + e.getMessage());
        }
    }

    private Mono<WechatLoginVO> createNewMember(String unionId, String openid, String sessionKey, String phoneCode) {
        log.info("开始创建新会员, unionId: {}, openid: {}", unionId, openid);

        String memberNo = MemberNoGenerator.generate();
        log.info("生成会员号: {}", memberNo);

        Member member = Member.builder()
                .memberNo(memberNo)
                .unionId(unionId)
                .miniappOpenId(openid)
                .lastLoginAt(LocalDateTime.now())
                .build();

        log.info("用户未注册，创建新会员（仅保存标识信息）");

        return memberRepository.save(member)
                .doOnSuccess(memberSyncer::sync)
                .flatMap(savedMember -> {
                    log.info("保存 Member 成功, id: {}, memberNo: {}", savedMember.getId(), savedMember.getMemberNo());
                    if (phoneCode != null && !phoneCode.isEmpty()) {
                        log.info("检测到 phoneCode，尝试获取手机号");
                        return wechatPhoneUtil.getPhoneNumber(phoneCode)
                                .flatMap(phoneNumber -> {
                                    if (phoneNumber != null && !phoneNumber.isEmpty()) {
                                        log.info("获取到手机号: {}", phoneNumber);
                                        String encryptedPhone = encryptPhone(phoneNumber);
                                        savedMember.setPhone(encryptedPhone);
                                        return memberRepository.save(savedMember)
                                                .doOnSuccess(memberSyncer::sync)
                                                .doOnSuccess(m -> {
                                                    log.info("新用户手机号绑定成功");
                                                })
                                                .thenReturn(buildLoginResponse(savedMember, true, sessionKey));
                                    } else {
                                        log.warn("未获取到手机号");
                                        return Mono.just(buildLoginResponse(savedMember, true, sessionKey));
                                    }
                                });
                    } else {
                        return Mono.just(buildLoginResponse(savedMember, true, sessionKey));
                    }
                });
    }

    private WechatLoginVO buildLoginResponse(Member member, boolean isNewUser, String sessionKey) {
        log.debug("构建登录响应, memberId: {}, isNewUser: {}", member.getId(), isNewUser);

        boolean needCompleteInfo = member.getNickname() == null || member.getPhone() == null;
        if (needCompleteInfo) {
            log.info("用户需要补全信息: nickname={}, phone={}",
                    member.getNickname() != null ? "已有" : "未设置",
                    member.getPhone() != null ? "已绑定" : "未绑定");
        }

        List<String> roles = new ArrayList<>();
        String accessToken = jwtTokenProvider.generateToken(String.valueOf(member.getId()), member.getId(), roles);

        log.info("JWT Token 生成成功, memberId: {}", member.getId());

        int expiresIn = 86400;

        return WechatLoginVO.builder()
                .memberId(member.getId())
                .accessToken(accessToken)
                .refreshToken(accessToken)
                .expiresIn(expiresIn)
                .isNewUser(isNewUser)
                .needCompleteInfo(needCompleteInfo)
                .build();
    }
}
