package top.tqx.demo_1.service;

import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import top.tqx.demo_1.config.JwtProperties;
import top.tqx.demo_1.entity.Role;
import top.tqx.demo_1.entity.User;
import top.tqx.demo_1.mapper.UserMapper;
import top.tqx.demo_1.mapper.UserRoleMapper;
import top.tqx.demo_1.util.RedisUtil;
import top.tqx.demo_1.vo.LoginResponse;
import top.tqx.demo_1.vo.UserInfoVO;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Slf4j
public class AuthService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired(required = false)
    private RedisUtil redisUtil;

    private SecretKey secretKey;
    
    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    @PostConstruct
    public void init() {
        this.secretKey = Keys.hmacShaKeyFor(jwtProperties.getSecret().getBytes(StandardCharsets.UTF_8));
    }

    public LoginResponse login(String username, String password) {
        User user = userMapper.selectOne(
                new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
                        .eq(User::getUsername, username)
        );

        if (user == null) {
            log.warn("登录失败 - 用户不存在: {}", username);
            throw new RuntimeException("用户名或密码错误");
        }

        if (!passwordEncoder.matches(password, user.getPassword())) {
            log.warn("登录失败 - 密码错误: {}", username);
            throw new RuntimeException("用户名或密码错误");
        }

        if ("0".equals(user.getStatus())) {
            log.warn("登录失败 - 用户已被禁用: {}", username);
            throw new RuntimeException("用户已被禁用");
        }

        String token = generateToken(user);
        String refreshToken = generateRefreshToken(user);

        LoginResponse response = new LoginResponse();
        response.setToken(token);
        response.setRefreshToken(refreshToken);
        response.setExpiresIn(jwtProperties.getExpiration());
        response.setUserInfo(convertToUserInfoVO(user));

        log.info("用户登录成功: {}", username);
        return response;
    }

    public void logout(String token) {
        try {
            var claims = Jwts.parser()
                    .verifyWith(secretKey)
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();
            
            Date expiration = claims.getExpiration();
            long ttl = expiration.getTime() - System.currentTimeMillis();
            
            if (ttl > 0 && redisUtil != null) {
                try {
                    String blacklistKey = "token:blacklist:" + token;
                    redisUtil.setIfAbsent(blacklistKey, "1", ttl, TimeUnit.MILLISECONDS);
                    log.info("Token 已加入黑名单");
                } catch (Exception e) {
                    log.debug("Redis 设置 Token 黑名单失败（Redis可能未启动）: {}", e.getMessage());
                }
            }
        } catch (Exception e) {
            log.warn("Token 解析失败", e);
        }
    }

    public UserInfoVO getUserInfo(Long userId) {
        User user = userMapper.selectById(userId);
        return convertToUserInfoVO(user);
    }

    public List<String> getUserRoleNames(Long userId) {
        List<Role> roles = userRoleMapper.selectRolesByUserId(userId);
        return roles.stream()
                .map(Role::getRoleName)
                .collect(Collectors.toList());
    }

    public String getUserMainRoleName(Long userId) {
        List<String> roleNames = getUserRoleNames(userId);
        return roleNames.isEmpty() ? "普通用户" : roleNames.get(0);
    }

    public User validateToken(String token) {
        try {
            if (redisUtil != null) {
                try {
                    String blacklistKey = "token:blacklist:" + token;
                    if (redisUtil.hasKey(blacklistKey)) {
                        log.warn("Token 已在黑名单中");
                        return null;
                    }
                } catch (Exception e) {
                    log.debug("Redis 检查 Token 黑名单失败（Redis可能未启动）: {}", e.getMessage());
                }
            }
            
            var claims = Jwts.parser()
                    .verifyWith(secretKey)
                    .build()
                    .parseSignedClaims(token)
                    .getPayload();

            Long userId = Long.parseLong(claims.getSubject());
            return userMapper.selectById(userId);
        } catch (Exception e) {
            return null;
        }
    }

    private String generateToken(User user) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + jwtProperties.getExpiration());

        return Jwts.builder()
                .subject(String.valueOf(user.getId()))
                .claim("username", user.getUsername())
                .issuedAt(now)
                .expiration(expiration)
                .signWith(secretKey)
                .compact();
    }

    private String generateRefreshToken(User user) {
        Date now = new Date();
        Date expiration = new Date(now.getTime() + jwtProperties.getExpiration() * 2);

        return Jwts.builder()
                .subject(String.valueOf(user.getId()))
                .issuedAt(now)
                .expiration(expiration)
                .signWith(secretKey)
                .compact();
    }

    private UserInfoVO convertToUserInfoVO(User user) {
        UserInfoVO vo = new UserInfoVO();
        vo.setId(user.getId());
        vo.setUsername(user.getUsername());
        vo.setRealName(user.getRealName());
        vo.setEmail(user.getEmail());
        vo.setPhone(user.getPhone());
        vo.setAvatar(user.getAvatar());
        vo.setUserType(user.getUserType());
        vo.setLastLoginTime(user.getLastLoginTime());
        return vo;
    }
}
