package com.bicloud.service.impl;

import com.bicloud.common.context.UserContext;
import com.bicloud.common.exception.BusinessException;
import com.bicloud.common.result.Result;
import com.bicloud.common.utils.JwtUtils;
import com.bicloud.common.utils.PasswordUtils;
import com.bicloud.common.utils.RegexUtils;
import com.bicloud.config.JwtProperties;
import com.bicloud.mapper.UserMapper;
import com.bicloud.pojo.dto.LoginDto;
import com.bicloud.pojo.dto.RegisterDto;
import com.bicloud.pojo.entity.User;
import com.bicloud.pojo.vo.LoginVo;
import com.bicloud.pojo.vo.UserInfoVo;
import com.bicloud.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

import static com.bicloud.common.utils.RedisConstants.*;


@Service
@Slf4j
public class UserServiceImpl implements UserService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private UserMapper userMapper;

    @Resource
    private JwtUtils jwtUtils;

    @Resource
    private JwtProperties jwtProperties;

    @Override
    public Result sendCode(String mobile, HttpSession session, HttpServletRequest request) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(mobile)) {
            // 2.如果不符合，返回错误信息
            return Result.error("手机号格式错误！");
        }

        // 3. 获取客户端IP地址
        String clientIpAddr = request.getRemoteAddr();

        // 4.如果存在锁，直接返回失败
        String phoneGetCodeLock = stringRedisTemplate.opsForValue().get(GET_CODE_LOCK + mobile);
        if (phoneGetCodeLock != null) {
            return Result.error("获取验证码过快，请稍后重试！");
        }

        // 5.检查两个黑名单中的次数
        String blacklistPhoneCount = stringRedisTemplate.opsForValue().get(GET_CODE_BLACKLIST_PHONE + mobile);
        int getCodePhoneCount = (blacklistPhoneCount != null) ? Integer.parseInt(blacklistPhoneCount) : 0;
        if (getCodePhoneCount >= 400) {
            return Result.error("获取验证码次数过多，您的手机号已被限制！");
        }

//        String blacklistIpAddrCount = stringRedisTemplate.opsForValue().get(GET_CODE_BLACKLIST_IP_ADDR + clientIpAddr);
//        int getCodeIpAddrCount = (blacklistIpAddrCount != null) ? Integer.parseInt(blacklistIpAddrCount) : 0;
//        if (getCodeIpAddrCount >= 300) {
//            return Result.error("获取验证码次数过多，您的IP已被限制！");
//        }

        // 6.更新锁和黑名单
        stringRedisTemplate.opsForValue().set(GET_CODE_BLACKLIST_PHONE + mobile, String.valueOf(getCodePhoneCount + 1), REFRESH_BLACKLIST_TTL, TimeUnit.HOURS);
//        stringRedisTemplate.opsForValue().set(GET_CODE_BLACKLIST_IP_ADDR + clientIpAddr, String.valueOf(getCodeIpAddrCount + 1), REFRESH_BLACKLIST_TTL, TimeUnit.HOURS);
        stringRedisTemplate.opsForValue().set(GET_CODE_LOCK + mobile, "1", GET_CODE_LOCK_TTL, TimeUnit.MINUTES);

        // 7.校验通过，生成验证码
//        String code = RandomUtil.randomNumbers(6);
        String code = "123123"; //为了方便测试，改成固定值

        // 8.保存验证码到 redis，并上锁
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY + mobile, code, LOGIN_CODE_TTL, TimeUnit.MINUTES);

        // 9.发送验证码
        log.debug("发送短信验证码成功，验证码：{}", code);
        // 10.返回ok
        return Result.success("发送短信验证码成功，验证码："+code);
    }

    @Override
    public Result register(RegisterDto registerDto) {
        // 1. 校验两次密码是否一致
        if (!registerDto.getPwd().equals(registerDto.getCpwd())) {
            return Result.error("两次密码输入不一致！");
        }

        // 2. 校验验证码
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY + registerDto.getMobile());
        if (cacheCode == null) {
            return Result.error("验证码已过期，请重新获取！");
        }
        if (!cacheCode.equals(registerDto.getVcode())) {
            return Result.error("验证码错误！");
        }

        // 3. 检查手机号是否已注册
        User existingUserByPhone = userMapper.selectByMobile(registerDto.getMobile());
        if (existingUserByPhone != null) {
            return Result.error("该手机号已被注册！");
        }

        // 4.检查邮箱是否已注册
        User existingUserByEmail = userMapper.selectByEmail(registerDto.getEmail());
        if (existingUserByEmail != null) {
            return Result.error("该邮箱已被注册！");
        }
        // 检查用户名是否已注册
        User existingUserByUsername = userMapper.selectByUsername(registerDto.getUsername());
        if (existingUserByUsername != null) {
            return Result.error("该用户名已被注册！");
        }

        // 5. 生成盐值
        String salt = PasswordUtils.generateSalt();

        // 6. 加密密码
        String encryptedPassword = PasswordUtils.encryptPassword(registerDto.getPwd(), salt);

        // 7. 将感兴趣标签数组转换为字符串
        String introduce = (registerDto.getInterestTags() != null && !registerDto.getInterestTags().isEmpty())
                ? String.join(",", registerDto.getInterestTags())
                : null;

        // 8. 创建用户对象
        User user = new User();
        user.setUsername(registerDto.getUsername());
        user.setCompany(registerDto.getCompany());
        user.setJob(registerDto.getJob());
        user.setAddress(registerDto.getAddress());
        user.setIntroduce(introduce);
        user.setMobile(registerDto.getMobile());
        user.setEmail(registerDto.getEmail());
        user.setPwd(encryptedPassword);
        user.setSalt(salt);
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());

        // 9. 保存用户到数据库
        int result = userMapper.insert(user);
        if (result <= 0) {
            return Result.error("注册失败，请稍后重试！");
        }

        // 10. 删除Redis中的验证码
        stringRedisTemplate.delete(LOGIN_CODE_KEY + registerDto.getMobile());

        log.info("用户注册成功，手机号：{}", registerDto.getMobile());
        return Result.success("注册成功！");
    }

    @Override
    public LoginVo login(LoginDto loginDto) {
        String identifier = loginDto.getIdentifier();
        String password = loginDto.getPassword();

        // 1. 根据登录标识类型查询用户
        User user = null;
        if (RegexUtils.isPhoneValid(identifier)) {
            // 手机号登录
            user = userMapper.selectByMobile(identifier);
        } else if (RegexUtils.isEmailValid(identifier)) {
            // 邮箱登录
            user = userMapper.selectByEmail(identifier);
        } else {
            // 用户名登录
            user = userMapper.selectByUsername(identifier);
        }

        // 2. 验证用户是否存在
        if (user == null) {
            throw new BusinessException("用户不存在！");
        }

        // 3. 验证密码
        if (!PasswordUtils.verifyPassword(password, user.getSalt(), user.getPwd())) {
            throw new BusinessException("密码错误！");
        }

        // 4. 生成Token
        String accessToken = jwtUtils.generateAccessToken(user.getId(), user.getUsername());
        String refreshToken = jwtUtils.generateRefreshToken(user.getId(), user.getUsername());

        // 5. 构建返回结果
        return LoginVo.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .tokenType("Bearer")
                .expiresIn(jwtProperties.getAccessTokenExpiration())
                .userId(user.getId())
                .username(user.getUsername())
                .build();
    }

    @Override
    public String refreshToken(String refreshToken) {
        // 1. 验证RefreshToken
        if (!jwtUtils.validateToken(refreshToken)) {
            throw new BusinessException("RefreshToken无效或已过期！");
        }

        // 2. 检查黑名单
        String blacklistKey = JWT_BLACKLIST_KEY + refreshToken;
        Boolean isBlacklisted = stringRedisTemplate.hasKey(blacklistKey);
        if (Boolean.TRUE.equals(isBlacklisted)) {
            throw new BusinessException("Token已失效，请重新登录！");
        }

        // 3. 从Token中获取用户信息
        Long userId = jwtUtils.getUserIdFromToken(refreshToken);
        String username = jwtUtils.getUsernameFromToken(refreshToken);

        if (userId == null || username == null) {
            throw new BusinessException("Token解析失败！");
        }

        // 4. 生成新的AccessToken
        return jwtUtils.generateAccessToken(userId, username);
    }

    @Override
    public void logout(String token) {
        // 1. 将Token加入黑名单
        String blacklistKey = JWT_BLACKLIST_KEY + token;
        Long remainingTime = jwtUtils.getTokenRemainingTime(token);

        // 2. 设置黑名单过期时间为Token剩余有效时间
        stringRedisTemplate.opsForValue().set(
                blacklistKey,
                "1",
                remainingTime,
                TimeUnit.SECONDS
        );

        log.info("用户登出成功，Token已加入黑名单");
    }

    @Override
    public UserInfoVo getCurrentUserInfo() {
        // 1. 从上下文获取用户ID
        Long userId = UserContext.getUserId();
        if (userId == null) {
            throw new BusinessException("未登录！");
        }

        // 2. 查询用户信息
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在！");
        }

        // 3. 构建返回结果
        return UserInfoVo.builder()
                .id(user.getId())
                .username(user.getUsername())
                .phone(user.getMobile())
                .email(user.getEmail())
                .createTime(user.getCreateTime())
                .build();
    }


}
