package cn.meowrain.aioj.backend.userservice.service.impl;

import cn.hutool.crypto.digest.BCrypt;
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
import cn.meowrain.aioj.backend.framework.core.exception.ClientException;
import cn.meowrain.aioj.backend.framework.core.exception.ServiceException;
import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;
import cn.meowrain.aioj.backend.userservice.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.userservice.dao.entity.User;
import cn.meowrain.aioj.backend.userservice.dao.mapper.UserMapper;
import cn.meowrain.aioj.backend.userservice.dto.chains.context.UserRegisterRequestParamVerifyContext;

import cn.meowrain.aioj.backend.userservice.dto.req.ChangePasswordRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.req.UserProfileUpdateRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.req.UserRegisterRequestDTO;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.userservice.dto.resp.UserProfileRespDTO;
import cn.meowrain.aioj.backend.userservice.service.EmailService;
import cn.meowrain.aioj.backend.userservice.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserRegisterRequestParamVerifyContext userRegisterRequestParamVerifyContext;

    private final EmailService emailService;

    @Override
    public Long userRegister(UserRegisterRequestDTO request) {
        UserAuthRespDTO authInfoByUserAccount = findAuthInfoByUserAccount(request.getUserAccount());
        if (authInfoByUserAccount != null) {
            throw new ClientException("重复创建用户");
        }

        log.info("进行用户注册");
        userRegisterRequestParamVerifyContext.handler(ChainMarkEnums.USER_REGISTER_REQ_PARAM_VERIFY.getMarkName(),
                request);
        // 使用 BCrypt 加密密码
        Date now = new Date();
        String salt = BCrypt.gensalt();
        String encryptPassword = BCrypt.hashpw(request.getUserPassword(), salt);
        User user = new User().setUserAccount(request.getUserAccount())
                .setUserPassword(encryptPassword)
                .setUserRole("user")
                .setCreateTime(now)
                .setUpdateTime(now);
        try {
            // 需要修改表，使得用户名是唯一的
            this.save(user);
        } catch (DuplicateKeyException e) {
            log.error("重复创建用户");
            throw new ServiceException("用户名已存在", ErrorCode.SYSTEM_ERROR);
        }

        return user.getId();
    }

    @Override
    public UserAuthRespDTO findAuthInfoByUserAccount(String userAccount) {
        User one = this.lambdaQuery().eq(User::getUserAccount, userAccount).one();
        UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
        if (one != null) {
            BeanUtils.copyProperties(one, userAuthDTO);
            return userAuthDTO;
        }
        return null;
    }

    @Override
    public UserAuthRespDTO findAuthInfoByUserId(Long userId) {
        User one = this.lambdaQuery().eq(User::getId, userId).one();
        UserAuthRespDTO userAuthDTO = new UserAuthRespDTO();
        if (one != null) {
            BeanUtils.copyProperties(one, userAuthDTO);
            return userAuthDTO;
        }
        return null;

    }

    @Override
    public void sendEmailCode(String email) {
        emailService.sendVerifyCode(email);
    }

    @Override
    public void bindEmail(String email, String code) {
        if (email == null || code == null) {
            throw new ClientException("邮箱或验证码不能为空");
        }
        Long currentUserId = ContextHolderUtils.getCurrentUserId();
        // 检查用户是否存在
        User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
        if (user == null) {
            throw new ClientException("用户不存在");
        }


        // 验证邮箱验证码
        // 从redis里面获取验证码
        String verifyCodeInSystem = emailService.getVerifyCode(email);
        // 验证验证码是否匹配
        if (!verifyCodeInSystem.equals(code)) {
            throw new ClientException("验证码错误,请重新输入");
        }

        // 绑定邮箱
        user.setUserEmail(email);
        user.setUpdateTime(new Date());
        this.updateById(user);
    }

    @Override
    public void unbindEmail() {
        Long currentUserId = ContextHolderUtils.getCurrentUserId();
        User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
        if (user == null) {
            throw new ClientException("用户不存在");
        }

        if (user.getUserEmail() == null) {
            throw new ClientException("邮箱未绑定");
        }
        // 这种方法避免了查询整个对象，并且能精确控制 NULL 值的更新。
        boolean success = this.lambdaUpdate()
                // 设置 userEmail 为 NULL
                .set(User::getUserEmail, null)
                // 设置 updateTime 为当前时间
                .set(User::getUpdateTime, new Date())
                // 筛选条件：用户ID
                .eq(User::getId, currentUserId)
                // 执行更新
                .update();

        if (!success) {
             throw new ClientException("解绑失败，请重试");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setProfileAvatar(Long fileId) {
        Long currentUserId = ContextHolderUtils.getCurrentUserId();
        User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
        user.setUserAvatar(fileId);
        user.setUpdateTime(new Date());
        this.updateById(user);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void changePassword(ChangePasswordRequestDTO dto) {
        Long currentUserId = ContextHolderUtils.getCurrentUserId();
        User user = this.lambdaQuery().eq(User::getId, currentUserId).one();

        // 检查用户是否存在
        if (user == null) {
            throw new ClientException("用户不存在");
        }

        // 检查旧密码是否正确
        String oldPassword = user.getUserPassword();
        if (!BCrypt.checkpw(dto.getOldPassword(), oldPassword)) {
            throw new ClientException("旧密码错误");
        }

        // 检查新密码是否与旧密码相同
        if (BCrypt.checkpw(dto.getNewPassword(), oldPassword)) {
            throw new ClientException("新密码不能与旧密码相同");
        }


        // 加密新密码
        Date now = new Date();
        String salt = BCrypt.gensalt();
        String encryptPassword = BCrypt.hashpw(dto.getNewPassword(), salt);
        // 更新用户密码
        user.setUserPassword(encryptPassword);
        user.setUpdateTime(now);
        this.updateById(user);
    }

    @Override
    public void updateUserProfile(UserProfileUpdateRequestDTO dto) {
        Long currentUserId = ContextHolderUtils.getCurrentUserId();
        User user = this.lambdaQuery().eq(User::getId, currentUserId).one();
        if (user == null) {
            throw new ClientException("用户不存在");
        }
        // 更新用户个人资料
        user.setUserName(dto.getUserName());
        user.setUserProfile(dto.getUserProfile());
        user.setUpdateTime(new Date());
        this.updateById(user);
    }

    /**
     * 获取用户个人资料
     * @return 用户个人资料
     */
    @Override
    public UserProfileRespDTO getUserProfile() {
        // TODO: 待实现 从数据库查询用户个人资料
        return null;
    }

}
