package com.greenorange.promotion.service.userInfo.impl;

import cn.hutool.core.util.RandomUtil;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.greenorange.promotion.common.ErrorCode;
import com.greenorange.promotion.config.WxOpenConfig;
import com.greenorange.promotion.constant.CommonConstant;
import com.greenorange.promotion.constant.SystemConstant;
import com.greenorange.promotion.constant.UserConstant;
import com.greenorange.promotion.exception.ThrowUtils;
import com.greenorange.promotion.mapper.UserInfoMapper;
import com.greenorange.promotion.model.dto.advancementApply.AdvancementApplyApproveRequest;
import com.greenorange.promotion.model.dto.projectCommission.ProjectCommissionAddRequest;
import com.greenorange.promotion.model.dto.userInfo.*;
import com.greenorange.promotion.model.entity.*;
import com.greenorange.promotion.model.enums.ReviewStatusEnum;
import com.greenorange.promotion.model.enums.UserRoleEnum;
import com.greenorange.promotion.service.common.CommonService;
import com.greenorange.promotion.service.project.ProjectCommissionService;
import com.greenorange.promotion.service.project.ProjectDetailService;
import com.greenorange.promotion.service.project.SubUserProjectCommissionService;
import com.greenorange.promotion.service.userInfo.AdvancementApplyService;
import com.greenorange.promotion.service.userInfo.UserInfoService;
import com.greenorange.promotion.service.userInfo.UserMainInfoService;
import com.greenorange.promotion.service.wechat.WechatGetQrcodeService;
import com.greenorange.promotion.utils.JWTUtils;
import com.greenorange.promotion.utils.RegexUtils;
import com.greenorange.promotion.utils.SendSmsUtil;
import com.greenorange.promotion.utils.SqlUtils;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* @author 35880
* @description 针对表【user_info(用户基本信息表)】的数据库操作Service实现
* @createDate 2025-04-24 11:47:37
*/
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo>
    implements UserInfoService{

    @Resource
    private JWTUtils jwtUtils;


    @Resource
    private CommonService commonService;


    @Resource
    private UserMainInfoService userMainInfoService;


    @Resource
    private RedisTemplate<String, String> redisTemplate;


    @Resource
    private WechatGetQrcodeService wechatGetQrcodeService;


    @Resource
    private ProjectCommissionService projectCommissionService;


    @Resource
    private SubUserProjectCommissionService subUserProjectCommissionService;


    @Resource
    private UserInfoMapper userInfoMapper;


    @Resource
    private ProjectDetailService projectDetailService;


    @Resource
    private AdvancementApplyService advancementApplyService;






    /**
     * 获取查询条件
     */
    @Override
    public QueryWrapper<UserInfo> getQueryWrapper(UserInfoQueryRequest userInfoQueryRequest) {
        Long id = userInfoQueryRequest.getId();
        String phoneNumber = userInfoQueryRequest.getPhoneNumber();
        String sortField = userInfoQueryRequest.getSortField();
        String sortOrder = userInfoQueryRequest.getSortOrder();
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(phoneNumber), "phoneNumber", phoneNumber);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), sortField);
        return queryWrapper;
    }


    /**
     * web端用户登录
     */
    @Override
    public String userInfoLogin(UserInfoLoginRequest userInfoLoginRequest) {
        String userAccount = userInfoLoginRequest.getUserAccount();
        String userPassword = userInfoLoginRequest.getUserPassword();
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserInfo::getUserAccount, userAccount).eq(UserInfo::getUserPassword, userPassword);
        UserInfo userInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "用户不存在");

        String userRole = userInfo.getUserRole();
        UserRoleEnum userRoleEnum = UserRoleEnum.getEnumByValue(userRole);
        ThrowUtils.throwIf(!UserRoleEnum.ADMIN.equals(userRoleEnum) && !UserRoleEnum.BOSS.equals(userRoleEnum), ErrorCode.NO_AUTH_ERROR);

        Map<String, String> payload = new HashMap<>();
        payload.put("userAccount", userAccount);
        payload.put("userPassword", userPassword);
        return jwtUtils.generateToken(payload);
    }






    /**
     * 小程序用户注册
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userInfoMiniRegister(UserInfoRegisterRequest userInfoRegisterRequest) {
        String phoneNumber = userInfoRegisterRequest.getPhoneNumber();
        String verificationCode = userInfoRegisterRequest.getVerificationCode();
        // 校验用户手机号和验证码
        checkPhoneAndVerificationCode(phoneNumber, verificationCode, UserRoleEnum.USER);

        // 根据邀请码获得上级用户信息
        String invitationCode = userInfoRegisterRequest.getInvitationCode();
        UserInfo parentUserInfo = getParentUserInfoByInvitationCode(invitationCode);

        // 保存用户
        UserInfo myUserInfo = commonService.copyProperties(userInfoRegisterRequest, UserInfo.class);
        myUserInfo.setParentUserId(parentUserInfo.getId());
        myUserInfo.setInvitationCode(RandomUtil.randomNumbers(6));
        myUserInfo.setUserAccount(phoneNumber);
        myUserInfo.setUserRole(UserConstant.DEFAULT_ROLE);
        myUserInfo.setUserAvatar(UserConstant.USER_DEFAULT_AVATAR);
        this.save(myUserInfo);

        // 批量更新父级用户团队人数
        UserMainInfo userMainInfo = updateParentUserInfoTeamCount(myUserInfo.getId());

        // 生成邀请二维码
        generateInvitationQrcode(userMainInfo, myUserInfo.getInvitationCode());

        // 批量保存当前用户的项目明细抽佣记录和下级用户项目明细抽佣记录
        saveBatchProjectCommissionAndSubUserProjectCommission(myUserInfo.getId(), parentUserInfo.getId());
    }



    /**
     * 小程序端用户密码登录
     */
    @Override
    public String userInfoMiniLoginByPwd(UserInfoMiniPasswordLoginRequest userInfoMiniPasswordLoginRequest) {
        String phoneNumber = userInfoMiniPasswordLoginRequest.getPhoneNumber();
        ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phoneNumber), ErrorCode.PARAMS_ERROR, "手机号格式无效");
        String userPassword = userInfoMiniPasswordLoginRequest.getUserPassword();
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserInfo::getPhoneNumber, phoneNumber);
        UserInfo userInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "手机号未注册");
        lambdaQueryWrapper.eq(UserInfo::getUserPassword, userPassword);
        userInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "密码不正确");

        Map<String, String> payload = new HashMap<>();
        payload.put("userAccount", phoneNumber);
        payload.put("userPassword", userPassword);
        return jwtUtils.generateToken(payload);
    }




    /**
     * 小程序用户验证码登录
     */
    @Override
    public String userInfoMiniLoginByVcd(UserInfoMiniVerifyCodeLoginRequest userInfoMiniVerifyCodeLoginRequest) {
        String phoneNumber = userInfoMiniVerifyCodeLoginRequest.getPhoneNumber();
        String verificationCode = userInfoMiniVerifyCodeLoginRequest.getVerificationCode();
        // 校验用户手机号和验证码
        checkPhoneAndVerificationCode(phoneNumber, verificationCode, null);

        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserInfo::getPhoneNumber, phoneNumber);
        UserInfo userInfo = this.getOne(lambdaQueryWrapper);
        String userPassword = userInfo.getUserPassword();

        Map<String, String> payload = new HashMap<>();
        payload.put("userAccount", phoneNumber);
        payload.put("userPassword", userPassword);
        return jwtUtils.generateToken(payload);
    }



    /**
     * 小程序用户重置密码
     */
    @Override
    public void userInfoMiniResetPwd(UserInfoResetRequest userInfoResetRequest, boolean isInner) {
        String phoneNumber = userInfoResetRequest.getPhoneNumber();
        String verificationCode = userInfoResetRequest.getVerificationCode();
        // 校验用户手机号和验证码
        checkPhoneAndVerificationCode(phoneNumber, verificationCode, null);

        String userPassword = userInfoResetRequest.getUserPassword();
        String userConfirmPassword = userInfoResetRequest.getUserConfirmPassword();
        ThrowUtils.throwIf(!userPassword.equals(userConfirmPassword), ErrorCode.OPERATION_ERROR, "两次密码不一致");

        // 更新用户密码
        LambdaUpdateWrapper<UserInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(UserInfo::getPhoneNumber, phoneNumber).set(UserInfo::getUserPassword, userPassword);
        this.update(lambdaUpdateWrapper);

        if (isInner) {
            // 获取token的过期时间
            String sourceToken = userInfoResetRequest.getSourceToken();
            DecodedJWT decodedJWT = jwtUtils.verify(sourceToken);
            long expirationTime = decodedJWT.getExpiresAt().getTime() - System.currentTimeMillis();
            // 将token存入Redis黑名单，并设置过期时间与token一致
            redisTemplate.opsForValue().set(sourceToken, sourceToken, expirationTime, TimeUnit.MILLISECONDS);
        }
    }


    /**
     * 小程序用户获取验证码（用于密码登录和忘记密码）
     */
    @Override
    public String getVerificationCodeForPwdLogin(String phoneNumber) {
        ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phoneNumber), ErrorCode.PARAMS_ERROR, "手机号格式错误");

        // 判断手机号是否已注册
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserInfo::getPhoneNumber, phoneNumber);
        UserInfo userInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(userInfo == null, ErrorCode.OPERATION_ERROR, "手机号未注册");

        String verificationCode = SendSmsUtil.getVerificationCode(phoneNumber);
        ThrowUtils.throwIf(verificationCode == null, ErrorCode.OPERATION_ERROR, "验证码获取失败");
        redisTemplate.opsForValue().set(SystemConstant.VERIFICATION_CODE + ":" + verificationCode, verificationCode, 5, TimeUnit.MINUTES);
        return verificationCode;
    }


    /**
     * 查询从 userId 一路到根节点的所有 id，按 depth 倒序（根先出）
     */
    @Override
    public List<Long> findPathToRoot(Long userId) {
        return userInfoMapper.findPathToRoot(userId);
    }



    /**
     * 小程序用户获取验证码（用于注册）
     */
    @Override
    public String getVerificationCodeForRegister(String phoneNumber) {
        ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phoneNumber), ErrorCode.PARAMS_ERROR, "手机号格式错误");

        // 判断手机号是否已注册
        LambdaQueryWrapper<UserInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(UserInfo::getPhoneNumber, phoneNumber);
        UserInfo userInfo = this.getOne(lambdaQueryWrapper);
        ThrowUtils.throwIf(userInfo != null, ErrorCode.OPERATION_ERROR, "手机号已注册");

        String verificationCode = SendSmsUtil.getVerificationCode(phoneNumber);
        ThrowUtils.throwIf(verificationCode == null, ErrorCode.OPERATION_ERROR, "验证码获取失败");
        redisTemplate.opsForValue().set(SystemConstant.VERIFICATION_CODE + ":" + verificationCode, verificationCode, 5, TimeUnit.MINUTES);
        return verificationCode;
    }


    /**
     * 校验用户手机号和验证码
     */
    @Override
    public void checkPhoneAndVerificationCode(String phoneNumber, String verificationCode, UserRoleEnum userRoleEnum) {
        ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phoneNumber), ErrorCode.PARAMS_ERROR, "手机号格式无效");
        if (userRoleEnum != null) {
            LambdaQueryWrapper<UserInfo> phoneNumberLambdaQueryWrapper = new LambdaQueryWrapper<>();
            phoneNumberLambdaQueryWrapper.eq(UserInfo::getPhoneNumber, phoneNumber);
            phoneNumberLambdaQueryWrapper = getQueryWrapperByUserRole(userRoleEnum, phoneNumberLambdaQueryWrapper);
            UserInfo userInfo = this.getOne(phoneNumberLambdaQueryWrapper);
            ThrowUtils.throwIf(userInfo != null, ErrorCode.OPERATION_ERROR, "手机号已注册");
        }
        String code = redisTemplate.opsForValue().get(SystemConstant.VERIFICATION_CODE + ":" + verificationCode);
        ThrowUtils.throwIf(code == null, ErrorCode.OPERATION_ERROR, "验证码已失效");

//        // 移除验证码
//        redisTemplate.delete(SystemConstant.VERIFICATION_CODE + ":" + verificationCode);
    }


    /**
     * 根据用户权限来获取查询条件
     */
    @Override
    public LambdaQueryWrapper<UserInfo> getQueryWrapperByUserRole(UserRoleEnum userRoleEnum, LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper) {
        if (userRoleEnum.equals(UserRoleEnum.USER)) {
            userInfoLambdaQueryWrapper.eq(UserInfo::getUserRole, userRoleEnum.getValue());
        } else {
            userInfoLambdaQueryWrapper.in(UserInfo::getUserRole, UserRoleEnum.MANAGER, UserRoleEnum.SUPERVISOR, UserRoleEnum.STAFF);
        }
        return userInfoLambdaQueryWrapper;
    }


    /**
     * 小程序端员工信息注册
     */
    @Override
    public void staffUserInfoMiniRegister(AdvancementApplyApproveRequest advancementApplyApproveRequest) {
        // 获取晋升申请记录信息
        Long applyId = advancementApplyApproveRequest.getApplyId();
        AdvancementApply advancementApply = advancementApplyService.getById(applyId);
        String phoneNumber = advancementApply.getPhone();
        ThrowUtils.throwIf(RegexUtils.isPhoneInvalid(phoneNumber), ErrorCode.PARAMS_ERROR, "手机号格式无效");

        // 根据邀请码获得上级用户信息
        String invitationCode = advancementApplyApproveRequest.getInvitationCode();
        UserInfo parentUserInfo = getParentUserInfoByInvitationCode(invitationCode);

        // 添加用户
        UserInfo myUserInfo = UserInfo.builder()
                .id(null)
                .nickName(advancementApply.getName())
                .phoneNumber(advancementApply.getPhone())
                .userAvatar(UserConstant.USER_DEFAULT_AVATAR)
                .userAccount(RandomUtil.randomNumbers(12))
                .userPassword(RandomUtil.randomString(12))
                .invitationCode(RandomUtil.randomNumbers(6))
                .userRole(advancementApplyApproveRequest.getUserRole())
                .parentUserId(parentUserInfo.getId())
                .build();
        this.save(myUserInfo);

        // 批量更新父级用户团队人数
        UserMainInfo userMainInfo = updateParentUserInfoTeamCount(myUserInfo.getId());

        // 生成邀请二维码
        generateInvitationQrcode(userMainInfo, myUserInfo.getInvitationCode());

        // 批量保存当前用户的项目明细抽佣记录和下级用户项目明细抽佣记录
        saveBatchProjectCommissionAndSubUserProjectCommission(myUserInfo.getId(), parentUserInfo.getId());

        // 修改晋升申请记录的审核状态，并绑定申请成功的用户id
        LambdaUpdateWrapper<AdvancementApply> advancementApplyLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        advancementApplyLambdaUpdateWrapper.eq(AdvancementApply::getId, advancementApply.getId())
                .set(AdvancementApply::getReviewStatus, ReviewStatusEnum.APPROVED.getValue())
                .set(AdvancementApply::getUserId, myUserInfo.getId());
        advancementApplyService.update(advancementApplyLambdaUpdateWrapper);
    }


    /**
     * 获取小程序用户的查询条件
     */
    @Override
    public LambdaQueryWrapper<UserInfo> getMiniUserInfoQueryWrapper() {
        LambdaQueryWrapper<UserInfo> userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userInfoLambdaQueryWrapper.in(UserInfo::getUserRole, UserRoleEnum.USER, UserRoleEnum.MANAGER, UserRoleEnum.SUPERVISOR, UserRoleEnum.STAFF);
        return userInfoLambdaQueryWrapper;
    }


    /**
     * 根据邀请码获得上级用户信息
     */
    private UserInfo getParentUserInfoByInvitationCode(String invitationCode) {
        LambdaQueryWrapper<UserInfo> invitedLambdaQueryWrapper = new LambdaQueryWrapper<>();
        invitedLambdaQueryWrapper.eq(UserInfo::getInvitationCode, invitationCode);
        UserInfo parentUserInfo = this.getOne(invitedLambdaQueryWrapper);
        ThrowUtils.throwIf(parentUserInfo == null, ErrorCode.OPERATION_ERROR, "邀请码错误");
        return parentUserInfo;
    }


    /**
     *  批量更新父级用户团队人数
     */
    private UserMainInfo updateParentUserInfoTeamCount(Long userId) {
        UserMainInfo userMainInfo = new UserMainInfo();
        userMainInfo.setUserId(userId);
        List<Long> pathToRoot = this.findPathToRoot(userId);
        List<UserMainInfo> userMainInfoList = commonService.findByFieldInTargetField(pathToRoot, userMainInfoService, id -> id, UserMainInfo::getUserId);
        for (UserMainInfo mainInfo : userMainInfoList) {
            mainInfo.setTeamSize(mainInfo.getTeamSize() + 1);
        }
        userMainInfoService.updateBatchById(userMainInfoList);
        return userMainInfo;
    }


    /**
     * 生成邀请二维码
     */
    private void generateInvitationQrcode(UserMainInfo userMainInfo, String invitationCode) {
        try {
            String view = wechatGetQrcodeService.getWxQrCode(invitationCode);
            userMainInfo.setInviteQrCode(view);
        } catch (IOException e) {
            e.printStackTrace();
        }
        userMainInfoService.save(userMainInfo);
    }


    /**
     * 批量保存当前用户的项目明细抽佣记录和下级用户项目明细抽佣记录
     */
    private void saveBatchProjectCommissionAndSubUserProjectCommission(Long userId, Long parentUserId) {
        // 查询上级用户的项目抽佣记录
        List<ProjectCommission> projectCommissionList = commonService.findByFieldEqTargetField(ProjectCommission::getUserId, parentUserId, projectCommissionService);
        // 封装Map集合（键：项目明细id, 值：项目最小价格）
        Map<Long, BigDecimal> projectDetailMinPriceMap = new HashMap<>();
        List<ProjectDetail> projectDetailList = commonService.findByFieldInTargetField(projectCommissionList, projectDetailService, ProjectCommission::getProjectDetailId, ProjectDetail::getId);
        for (ProjectDetail projectDetail : projectDetailList) {
            projectDetailMinPriceMap.put(projectDetail.getId(), projectDetail.getProjectMinSettlementPrice());
        }
        // 插入当前用户的项目抽佣记录
        List<ProjectCommission> projectCommissions = new ArrayList<>();
        for (ProjectCommission projectCommission : projectCommissionList) {
            ProjectCommissionAddRequest projectCommissionAddRequest = commonService.copyProperties(projectCommission, ProjectCommissionAddRequest.class);
            ProjectCommission proCommission = commonService.copyProperties(projectCommissionAddRequest, ProjectCommission.class);
            // 获取当前项目明细的最小结算价格
            BigDecimal projectMinSettlementPrice = projectDetailMinPriceMap.get(projectCommission.getProjectDetailId());
            BigDecimal currentSettlementPrice = projectCommission.getMyUnitPrice().multiply(BigDecimal.ONE.subtract(projectCommission.getCurrentCommissionRate().divide(BigDecimal.valueOf(100))));
            // 比较当前结算价格和项目明细最小结算价格，取出最大值
            proCommission.setMyUnitPrice(projectMinSettlementPrice.max(currentSettlementPrice));
            proCommission.setCurrentCommissionRate(BigDecimal.ZERO);
            proCommission.setUserId(userId);
            projectCommissions.add(proCommission);
        }
        projectCommissionService.saveBatch(projectCommissions);
        // 插入下级用户的项目明细抽佣记录
        List<SubUserProjectCommission> subUserProjectCommissionList = new ArrayList<>();
        for (ProjectCommission projectCommission : projectCommissionList) {
            ProjectCommissionAddRequest projectCommissionAddRequest = commonService.copyProperties(projectCommission, ProjectCommissionAddRequest.class);
            SubUserProjectCommission subUserProjectCommission = commonService.copyProperties(projectCommissionAddRequest, SubUserProjectCommission.class);
            subUserProjectCommission.setSubUserId(userId);
            subUserProjectCommissionList.add(subUserProjectCommission);
        }
        subUserProjectCommissionService.saveBatch(subUserProjectCommissionList);
    }






}




