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

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.crypto.digest.BCrypt;
import cn.meowrain.aioj.backend.auth.clients.UserClient;
import cn.meowrain.aioj.backend.auth.common.constants.RedisKeyConstants;
import cn.meowrain.aioj.backend.auth.common.enums.ChainMarkEnums;
import cn.meowrain.aioj.backend.framework.core.utils.ContextHolderUtils;
import cn.meowrain.aioj.backend.framework.security.properties.JwtPropertiesConfiguration;
import cn.meowrain.aioj.backend.auth.dto.chains.context.UserLoginRequestParamVerifyContext;
import cn.meowrain.aioj.backend.auth.dto.req.UserLoginRequestDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserAuthRespDTO;
import cn.meowrain.aioj.backend.auth.dto.resp.UserLoginResponseDTO;
import cn.meowrain.aioj.backend.auth.service.AuthService;
import cn.meowrain.aioj.backend.auth.utils.AuthServiceJwtUtil;
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.web.Result;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {

    private final AuthServiceJwtUtil jwtUtil;

    private final UserLoginRequestParamVerifyContext userLoginRequestParamVerifyContext;

    private final UserClient userClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final JwtPropertiesConfiguration jwtPropertiesConfiguration;

    @Override
    public UserLoginResponseDTO userLogin(UserLoginRequestDTO requestParam) {
        log.info("用户登录请求: userAccount={}", requestParam.getUserAccount());

        // 1.校验
        userLoginRequestParamVerifyContext.handler(ChainMarkEnums.USER_LOGIN_REQ_PARAM_VERIFY.getMarkName(),
                requestParam);

        // 如果调用user-service失败，那么就说明是系统内部错误
        log.info("正在调用user-service查询用户信息...");
        Result<UserAuthRespDTO> userResp = userClient.getUserByUserName(requestParam.getUserAccount());

        if (userResp.isFail()) {
            log.error("调用user-service返回失败：{}", userResp.getMessage());
            throw new ServiceException(ErrorCode.SYSTEM_ERROR);
        }

        UserAuthRespDTO user = userResp.getData();
        if (user == null) {
            log.warn("用户不存在: {}", requestParam.getUserAccount());
            throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
        }

        if (!BCrypt.checkpw(requestParam.getUserPassword(), user.getUserPassword())) {
            log.warn("密码错误: {}", requestParam.getUserAccount());
            throw new ServiceException("用户不存在或密码错误", ErrorCode.NOT_LOGIN_ERROR);
        }

        // 生成 JWT
        log.info("正在生成JWT token...");
        String accessToken = jwtUtil.generateAccessToken(user);
        String refreshToken = jwtUtil.generateRefreshToken(user.getId());

        UserLoginResponseDTO resp = new UserLoginResponseDTO();
        resp.setId(user.getId());
        resp.setUserAccount(user.getUserAccount());
        resp.setAccessToken(accessToken);
        resp.setRefreshToken(refreshToken);
        resp.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
        resp.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
        // refresh token存入到REDIS里面
        stringRedisTemplate.opsForValue()
                .set(String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, user.getId()), refreshToken,
                        jwtPropertiesConfiguration.getRefreshExpire(), TimeUnit.MILLISECONDS);

        log.info("用户登录成功: userId={}, userAccount={}", user.getId(), user.getUserAccount());
        return resp;
    }

    /**
     * 更新access token，使用refresh token
     *
     * @param refreshToken
     * @return
     */
    @Override
    public UserLoginResponseDTO refreshToken(String refreshToken) {
        UserLoginResponseDTO userLoginResponseDTO = new UserLoginResponseDTO();
        if (!jwtUtil.isTokenValid(refreshToken)) {
            throw new ServiceException("Refresh Token 已过期");
        }

        Long userId = Long.valueOf(jwtUtil.parseClaims(refreshToken).getSubject());

        String cacheKey = String.format(RedisKeyConstants.REFRESH_TOKEN_KEY_PREFIX, userId);
        String cacheValue = stringRedisTemplate.opsForValue().get(cacheKey);

        if (cacheValue == null || !cacheValue.equals(refreshToken)) {
            throw new ServiceException(ErrorCode.NO_AUTH_ERROR);
        }

        // 再次签发新的 Access Token
        // 此处你需要查用户，拿 userName, role
        Result<UserAuthRespDTO> userResult = userClient.getUserById(userId);
        if (userResult.isFail()) {
            log.error("通过id查找用户失败:{}", userResult.getMessage());
            throw new ServiceException(ErrorCode.SYSTEM_ERROR);
        }
        UserAuthRespDTO user = userResult.getData();
        String newAccessToken = jwtUtil.generateAccessToken(user);

        // 设置refresh token和access token
        userLoginResponseDTO.setRefreshToken(refreshToken);
        userLoginResponseDTO.setAccessToken(newAccessToken);
        userLoginResponseDTO.setAccessTokenExpireTime(jwtPropertiesConfiguration.getAccessExpire());
        userLoginResponseDTO.setRefreshTokenExpireTime(jwtPropertiesConfiguration.getRefreshExpire());
        return userLoginResponseDTO;
    }

    /**
     * 验证token的有效性
     *
     * @param accessToken 访问令牌
     * @return token是否有效
     */
    @Override
    public Boolean validateToken(String accessToken) {
        try {
            // 1. 检查token格式
            if (accessToken == null || accessToken.trim().isEmpty()) {
                log.warn("Access token is null or empty");
                return false;
            }

            // 2. 验证token签名和过期时间
            if (!jwtUtil.isTokenValid(accessToken)) {
                log.warn("Access token is invalid or expired");
                return false;
            }

            // 3. 解析token获取用户信息
            String userId = jwtUtil.parseClaims(accessToken).getSubject();
            if (userId == null) {
                log.warn("Access token does not contain valid user id");
                return false;
            }

            // 4. 验证用户是否存在（可选，增加安全性）
            Result<UserAuthRespDTO> userResult = userClient.getUserById(Long.valueOf(userId));
            if (userResult.isFail() || userResult.getData() == null) {
                log.warn("User not found for id: {}", userId);
                return false;
            }

            log.debug("Access token validation successful for user: {}", userId);
            return true;
        } catch (Exception e) {
            log.error("Error validating access token", e);
            return false;
        }
    }

    @Override
    public UserAuthRespDTO getUserInfo() {
        Long currentUserId = ContextHolderUtils.getCurrentUserId();

        // 查询用户信息（IO 操作）
        Result<UserAuthRespDTO> userResult;
        try {
            userResult = userClient.getUserById(currentUserId);
        } catch (Exception e) {
            log.error("Failed to call user service", e);
            throw new ClientException(ErrorCode.SYSTEM_ERROR);
        }

        if (userResult == null || userResult.isFail()) {
            throw new ClientException(ErrorCode.SYSTEM_ERROR);
        }

        if (userResult.getData() == null) {
            throw new ClientException(ErrorCode.NOT_FOUND_ERROR);
        }

        return userResult.getData();
    }


}
