package com.chinaweal.aiccs.org.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.aiccs.common.util.JwtTokenUtil;
import com.chinaweal.aiccs.common.util.StringUtils;
import com.chinaweal.aiccs.org.entity.OauthAccessToken;
import com.chinaweal.aiccs.org.entity.OauthAuthorizationCode;
import com.chinaweal.aiccs.org.entity.dto.TokenRequestDTO;
import com.chinaweal.aiccs.org.entity.dto.TokenResponseDTO;
import com.chinaweal.aiccs.org.mapper.OauthAccessTokenMapper;
import com.chinaweal.aiccs.org.service.IOauthAccessTokenService;
import com.chinaweal.aiccs.org.service.IOauthAuthorizationCodeService;
import com.chinaweal.youfool.framework.springboot.exception.custom.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * OAuth令牌服务实现
 *
 * @author chinaweal
 * @since 2025-11-21
 */
@Slf4j
@Service
public class OauthAccessTokenServiceImpl extends ServiceImpl<OauthAccessTokenMapper, OauthAccessToken> implements IOauthAccessTokenService {

    @Autowired
    private IOauthAuthorizationCodeService authorizationCodeService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    /**
     * 访问令牌有效期（分钟）
     */
    @Value("${oauth.access-token.expire-minutes:60}")
    private int accessTokenExpireMinutes;

    /**
     * 刷新令牌有效期（天）
     */
    @Value("${oauth.refresh-token.expire-days:30}")
    private int refreshTokenExpireDays;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TokenResponseDTO createAccessToken(TokenRequestDTO tokenRequest, String userId, String userName) {
        log.info("开始创建访问令牌，授权类型: {}, 客户端ID: {}, 用户ID: {}",
                tokenRequest.getGrantType(), tokenRequest.getClientId(), userId);

        try {
            // 验证客户端信息
            validateClient(tokenRequest.getClientId(), tokenRequest.getClientSecret());

            String scope = tokenRequest.getScope();
            String grantType = tokenRequest.getGrantType();

            switch (grantType) {
                case "authorization_code":
                    return createTokenByAuthorizationCode(tokenRequest, userId, userName, scope);
                case "refresh_token":
                    return createTokenByRefreshToken(tokenRequest);
                case "client_credentials":
                    return createTokenByClientCredentials(tokenRequest);
                case "password":
                    return createTokenByPassword(tokenRequest, userName, scope);
                default:
                    throw new BusinessException("不支持的授权类型: " + grantType);
            }

        } catch (Exception e) {
            log.error("创建访问令牌失败，授权类型: {}, 客户端ID: {}",
                    tokenRequest.getGrantType(), tokenRequest.getClientId(), e);
            throw new BusinessException("创建访问令牌失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TokenResponseDTO refreshAccessToken(String refreshToken, String clientId) {
        log.info("开始刷新访问令牌，客户端ID: {}", clientId);

        if (StringUtils.isBlank(refreshToken)) {
            throw new BusinessException("刷新令牌不能为空");
        }

        try {
            // 根据刷新令牌查询令牌信息
            OauthAccessToken tokenInfo = baseMapper.selectByRefreshTokenAndClientId(refreshToken, clientId);

            if (tokenInfo == null) {
                throw new BusinessException("无效的刷新令牌");
            }

            // 检查令牌是否有效
            if (!"1".equals(tokenInfo.getIsActive())) {
                throw new BusinessException("刷新令牌已失效");
            }

            // 检查刷新令牌是否过期
            if (tokenInfo.getRefreshExpiresIn().isBefore(LocalDateTime.now())) {
                throw new BusinessException("刷新令牌已过期");
            }

            // 撤销旧的访问令牌
            baseMapper.revokeAccessToken(tokenInfo.getAccessToken());

            // 生成新的访问令牌
            String newAccessToken = jwtTokenUtil.generateAccessToken(
                    tokenInfo.getUserId(),
                    tokenInfo.getUserName(),
                    clientId,
                    tokenInfo.getScope(),
                    accessTokenExpireMinutes
            );

            // 计算过期时间
            LocalDateTime accessTokenExpiresAt = LocalDateTime.now().plusMinutes(accessTokenExpireMinutes);

            // 创建新的令牌记录（保留原有的刷新令牌）
            OauthAccessToken newToken = new OauthAccessToken()
                    .setTokenId(StringUtils.getUUID())
                    .setAccessToken(newAccessToken)
                    .setRefreshToken(refreshToken)
                    .setClientId(clientId)
                    .setUserId(tokenInfo.getUserId())
                    .setUserName(tokenInfo.getUserName())
                    .setScope(tokenInfo.getScope())
                    .setGrantType("refresh_token")
                    .setExpiresIn(accessTokenExpiresAt)
                    .setRefreshExpiresIn(tokenInfo.getRefreshExpiresIn())
                    .setIsActive("1");

            // 保存新令牌
            boolean saved = save(newToken);
            if (!saved) {
                throw new BusinessException("保存新令牌失败");
            }

            log.info("访问令牌刷新成功，用户ID: {}, 新令牌: {}", tokenInfo.getUserId(), newAccessToken);

            return new TokenResponseDTO()
                    .setAccessToken(newAccessToken)
                    .setTokenType("Bearer")
                    .setExpiresIn(accessTokenExpireMinutes * 60)
                    .setRefreshToken(refreshToken)
                    .setScope(tokenInfo.getScope());

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("刷新访问令牌失败，客户端ID: {}", clientId, e);
            throw new BusinessException("刷新访问令牌失败: " + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean revokeToken(String token, String tokenTypeHint) {
        log.info("开始撤销令牌，令牌类型提示: {}", tokenTypeHint);

        if (StringUtils.isBlank(token)) {
            log.warn("令牌为空");
            return false;
        }

        try {
            int updated = 0;

            // 根据令牌类型提示或自动识别来撤销令牌
            if ("refresh_token".equals(tokenTypeHint)) {
                // 明确指定是刷新令牌
                updated = baseMapper.revokeRefreshToken(token);
            } else if ("access_token".equals(tokenTypeHint)) {
                // 明确指定是访问令牌
                updated = baseMapper.revokeAccessToken(token);
            } else {
                // 自动识别令牌类型
                // 首先尝试作为访问令牌
                updated = baseMapper.revokeAccessToken(token);
                if (updated == 0) {
                    // 如果不是访问令牌，尝试作为刷新令牌
                    updated = baseMapper.revokeRefreshToken(token);
                }
            }

            boolean success = updated > 0;
            log.info("令牌撤销完成，令牌类型: {}, 结果: {}", tokenTypeHint, success);
            return success;

        } catch (Exception e) {
            log.error("撤销令牌失败，令牌类型: {}", tokenTypeHint, e);
            return false;
        }
    }

    @Override
    public OauthAccessToken validateAccessToken(String accessToken) {
        log.info("开始验证访问令牌");

        if (StringUtils.isBlank(accessToken)) {
            return null;
        }

        try {
            // 查询令牌信息
            OauthAccessToken tokenInfo = baseMapper.selectByAccessToken(accessToken);

            if (tokenInfo == null) {
                log.warn("未找到访问令牌信息");
                return null;
            }

            // 检查令牌是否有效
            if (!"1".equals(tokenInfo.getIsActive())) {
                log.warn("访问令牌已失效");
                return null;
            }

            // 检查令牌是否过期
            if (tokenInfo.getExpiresIn().isBefore(LocalDateTime.now())) {
                log.warn("访问令牌已过期，过期时间: {}", tokenInfo.getExpiresIn());
                // 自动标记为无效
                markTokenAsInactive(accessToken);
                return null;
            }

            // 验证JWT令牌签名和内容
            if (!jwtTokenUtil.validateToken(accessToken)) {
                log.warn("JWT令牌验证失败");
                return null;
            }

            log.info("访问令牌验证成功，用户ID: {}", tokenInfo.getUserId());
            return tokenInfo;

        } catch (Exception e) {
            log.error("验证访问令牌失败", e);
            return null;
        }
    }

    @Override
    public OauthAccessToken getAccessTokenByRefreshToken(String refreshToken) {
        log.info("根据刷新令牌获取令牌信息");

        if (StringUtils.isBlank(refreshToken)) {
            return null;
        }

        try {
            OauthAccessToken tokenInfo = baseMapper.selectByRefreshToken(refreshToken);

            if (tokenInfo != null) {
                log.info("获取令牌信息成功，用户ID: {}", tokenInfo.getUserId());
            } else {
                log.warn("未找到刷新令牌信息");
            }

            return tokenInfo;

        } catch (Exception e) {
            log.error("根据刷新令牌获取令牌信息失败", e);
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean invalidateAccessToken(String accessToken) {
        log.info("开始使访问令牌失效");

        if (StringUtils.isBlank(accessToken)) {
            return false;
        }

        try {
            int updated = baseMapper.revokeAccessToken(accessToken);
            boolean success = updated > 0;

            log.info("访问令牌失效完成，结果: {}", success);
            return success;

        } catch (Exception e) {
            log.error("使访问令牌失败", e);
            return false;
        }
    }

    /**
     * 通过授权码创建令牌
     */
    private TokenResponseDTO createTokenByAuthorizationCode(TokenRequestDTO tokenRequest, String userId, String userName, String scope) {
        // 验证授权码
        OauthAuthorizationCode authCode = authorizationCodeService.validateAuthorizationCode(
                tokenRequest.getCode(), tokenRequest.getClientId());

        if (authCode == null) {
            throw new BusinessException("无效的授权码");
        }

        // 使用授权码
        boolean consumed = authorizationCodeService.consumeAuthorizationCode(tokenRequest.getCode(), tokenRequest.getClientId());
        if (!consumed) {
            throw new BusinessException("授权码使用失败");
        }

        // 撤销该用户在该客户端的旧令牌
        baseMapper.revokeTokensByUserAndClient(userId, tokenRequest.getClientId());

        // 生成新的访问令牌和刷新令牌
        String accessToken = jwtTokenUtil.generateAccessToken(
                userId, userName, tokenRequest.getClientId(),
                scope != null ? scope : authCode.getScope(),
                accessTokenExpireMinutes
        );
        String refreshToken = jwtTokenUtil.generateRefreshToken();

        // 计算过期时间
        LocalDateTime accessTokenExpiresAt = LocalDateTime.now().plusMinutes(accessTokenExpireMinutes);
        LocalDateTime refreshTokenExpiresAt = LocalDateTime.now().plusDays(refreshTokenExpireDays);

        // 保存令牌信息
        OauthAccessToken token = new OauthAccessToken()
                .setTokenId(StringUtils.getUUID())
                .setAccessToken(accessToken)
                .setRefreshToken(refreshToken)
                .setClientId(tokenRequest.getClientId())
                .setUserId(userId)
                .setUserName(userName)
                .setScope(scope != null ? scope : authCode.getScope())
                .setGrantType("authorization_code")
                .setExpiresIn(accessTokenExpiresAt)
                .setRefreshExpiresIn(refreshTokenExpiresAt)
                .setIsActive("1");

        boolean saved = save(token);
        if (!saved) {
            throw new BusinessException("保存令牌失败");
        }

        return new TokenResponseDTO()
                .setAccessToken(accessToken)
                .setTokenType("Bearer")
                .setExpiresIn(accessTokenExpireMinutes * 60)
                .setRefreshToken(refreshToken)
                .setScope(scope != null ? scope : authCode.getScope());
    }

    /**
     * 通过客户端凭证创建令牌
     */
    private TokenResponseDTO createTokenByClientCredentials(TokenRequestDTO tokenRequest) {
        // 客户端凭证模式没有用户信息
        String accessToken = jwtTokenUtil.generateAccessToken(
                "client_" + tokenRequest.getClientId(),
                tokenRequest.getClientId(),
                tokenRequest.getClientId(),
                tokenRequest.getScope(),
                accessTokenExpireMinutes
        );
        String refreshToken = jwtTokenUtil.generateRefreshToken();

        LocalDateTime accessTokenExpiresAt = LocalDateTime.now().plusMinutes(accessTokenExpireMinutes);
        LocalDateTime refreshTokenExpiresAt = LocalDateTime.now().plusDays(refreshTokenExpireDays);

        OauthAccessToken token = new OauthAccessToken()
                .setTokenId(StringUtils.getUUID())
                .setAccessToken(accessToken)
                .setRefreshToken(refreshToken)
                .setClientId(tokenRequest.getClientId())
                .setUserId("client_" + tokenRequest.getClientId())
                .setUserName(tokenRequest.getClientId())
                .setScope(tokenRequest.getScope())
                .setGrantType("client_credentials")
                .setExpiresIn(accessTokenExpiresAt)
                .setRefreshExpiresIn(refreshTokenExpiresAt)
                .setIsActive("1");

        boolean saved = save(token);
        if (!saved) {
            throw new BusinessException("保存令牌失败");
        }

        return new TokenResponseDTO()
                .setAccessToken(accessToken)
                .setTokenType("Bearer")
                .setExpiresIn(accessTokenExpireMinutes * 60)
                .setRefreshToken(refreshToken)
                .setScope(tokenRequest.getScope());
    }

    /**
     * 通过密码模式创建令牌
     */
    private TokenResponseDTO createTokenByPassword(TokenRequestDTO tokenRequest, String userName, String scope) {
        String accessToken = jwtTokenUtil.generateAccessToken(
                "user_" + userName,
                userName,
                tokenRequest.getClientId(),
                scope,
                accessTokenExpireMinutes
        );
        String refreshToken = jwtTokenUtil.generateRefreshToken();

        LocalDateTime accessTokenExpiresAt = LocalDateTime.now().plusMinutes(accessTokenExpireMinutes);
        LocalDateTime refreshTokenExpiresAt = LocalDateTime.now().plusDays(refreshTokenExpireDays);

        OauthAccessToken token = new OauthAccessToken()
                .setTokenId(StringUtils.getUUID())
                .setAccessToken(accessToken)
                .setRefreshToken(refreshToken)
                .setClientId(tokenRequest.getClientId())
                .setUserId("user_" + userName)
                .setUserName(userName)
                .setScope(scope)
                .setGrantType("password")
                .setExpiresIn(accessTokenExpiresAt)
                .setRefreshExpiresIn(refreshTokenExpiresAt)
                .setIsActive("1");

        boolean saved = save(token);
        if (!saved) {
            throw new BusinessException("保存令牌失败");
        }

        return new TokenResponseDTO()
                .setAccessToken(accessToken)
                .setTokenType("Bearer")
                .setExpiresIn(accessTokenExpireMinutes * 60)
                .setRefreshToken(refreshToken)
                .setScope(scope);
    }

    /**
     * 通过刷新令牌创建令牌（已在refreshAccessToken方法中实现）
     */
    private TokenResponseDTO createTokenByRefreshToken(TokenRequestDTO tokenRequest) {
        // 直接调用刷新令牌方法
        return refreshAccessToken(tokenRequest.getRefreshToken(), tokenRequest.getClientId());
    }

    /**
     * 验证客户端信息
     */
    private void validateClient(String clientId, String clientSecret) {
        // TODO: 实现客户端验证逻辑
        // 这里需要查询oauth_client表验证客户端ID和密钥
        // 暂时跳过实际验证，在实际项目中需要实现
    }

    /**
     * 标记令牌为无效
     */
    private void markTokenAsInactive(String accessToken) {
        try {
            QueryWrapper<OauthAccessToken> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("access_token", accessToken);
            queryWrapper.eq("is_active", "1");

            OauthAccessToken updateEntity = new OauthAccessToken();
            updateEntity.setIsActive("0");
            updateEntity.setUpdateTime(LocalDateTime.now());

            update(updateEntity, queryWrapper);
            log.info("令牌标记为无效成功，访问令牌: {}", accessToken);
        } catch (Exception e) {
            log.error("标记令牌为无效失败，访问令牌: {}", accessToken, e);
        }
    }

    /**
     * 清理过期的令牌
     * 这个方法可以定期调用，例如通过定时任务
     */
    @Transactional(rollbackFor = Exception.class)
    public void cleanExpiredTokens() {
        log.info("开始清理过期的令牌");

        try {
            int updated = baseMapper.markExpiredAsInactive();
            log.info("过期的访问令牌清理完成，更新记录数: {}", updated);

            int refreshUpdated = baseMapper.markExpiredRefreshTokenAsInactive();
            log.info("过期的刷新令牌清理完成，更新记录数: {}", refreshUpdated);

        } catch (Exception e) {
            log.error("清理过期令牌失败", e);
            throw new BusinessException("清理过期令牌失败: " + e.getMessage());
        }
    }
}