package com.moka.gdtauto.service;

import java.time.LocalDateTime;
import java.util.List;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.moka.gdtauto.config.TencentAdsConfig;
import com.moka.gdtauto.entity.TencentAdsAuth;
import com.moka.gdtauto.mapper.TencentAdsAuthMapper;
import com.tencent.ads.ApiContextConfig;
import com.tencent.ads.exception.TencentAdsResponseException;
import com.tencent.ads.exception.TencentAdsSDKException;
import com.tencent.ads.model.v3.AuthorizerStruct;
import com.tencent.ads.model.v3.OauthTokenResponseData;
import com.tencent.ads.v3.TencentAds;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 腾讯广告OAuth授权服务
 * 按照官方SDK文档推荐方式实现
 * 
 * @author moka
 * @since 2026-02-03
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TencentAdsAuthService extends ServiceImpl<TencentAdsAuthMapper, TencentAdsAuth> {

    private final TencentAdsConfig tencentAdsConfig;

    private static final String CACHE_NAME = "tencentAdsAuth";
    /**
     * 获取授权URL
     * 
     * @param redirectUri 回调地址
     * @param state 状态参数，用于防止CSRF攻击
     * @return 授权URL
     */
    public String getAuthorizationUrl(String state) throws Exception {
        String baseUrl = "https://developers.e.qq.com/oauth/authorize";
        String redirectUri = tencentAdsConfig.getOAuthRedirectUri();
        redirectUri = java.net.URLEncoder.encode(redirectUri, "UTF-8");
        return String.format("%s?client_id=%s&account_type=ACCOUNT_TYPE_WECHAT&redirect_uri=%s&state=%s&scope=",
                baseUrl,
                tencentAdsConfig.getClientId(),
                redirectUri,
                state);
    }

    

    /**
     * 通过授权码获取访问令牌
     * 使用官方SDK推荐方式
     * 
     * @param authorizationCode 授权码
     * @param redirectUri 回调地址
     * @return 授权信息
     * @throws Exception API异常
     */
    @Transactional(rollbackFor = Exception.class)
    public TencentAdsAuth getAccessToken(String authorizationCode, String state) throws Exception {
        try {
            // 初始化TencentAds实例
            TencentAds tencentAds = TencentAds.getInstance();
            tencentAds.init(new ApiContextConfig().isDebug(true));
            
            // 调用OAuth API获取token
            Long clientId = Long.parseLong(tencentAdsConfig.getClientId());
            String clientSecret = tencentAdsConfig.getSecret();
            String grantType = "authorization_code";
            String redirectUri = tencentAdsConfig.getOauthOkRedirectUri();
            redirectUri = java.net.URLEncoder.encode(redirectUri, "UTF-8");
            
            // 按照SDK的方法签名调用
            OauthTokenResponseData responseData = tencentAds.oauth()
                .oauthToken(clientId, clientSecret, grantType, authorizationCode, null, redirectUri, null);
            
            if (responseData == null) {
                throw new Exception("获取token响应为空");
            }
            
            // 解析响应并保存
            return saveTokenData(responseData, state);
            
        } catch (TencentAdsResponseException e) {
            log.error("获取访问令牌失败 - API响应错误", e);
            throw new Exception("获取访问令牌失败：" + e.getMessage());
        } catch (TencentAdsSDKException e) {
            log.error("获取访问令牌失败 - SDK错误", e);
            throw new Exception("获取访问令牌失败：" + e.getMessage());
        }
    }

    /**
     * 刷新访问令牌
     * 使用官方SDK推荐方式
     * 
     * @param accountId 账户ID
     * @return 更新后的授权信息
     * @throws Exception API异常
     */
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = CACHE_NAME, key = "#result.accountId")
    public TencentAdsAuth refreshAccessToken(Long id) throws Exception {
        TencentAdsAuth auth = getById(id);
        if (auth == null) {
            throw new Exception("未找到账户授权信息：" + id);
        }
        
        return refreshAccessToken(auth);
    }

    /**
     * 刷新访问令牌
     * 
     * @param auth 授权信息
     * @return 更新后的授权信息
     * @throws Exception API异常
     */
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = CACHE_NAME, key = "#auth.accountId")
    public TencentAdsAuth refreshAccessToken(TencentAdsAuth auth) throws Exception {
        try {
            // 初始化TencentAds实例
            TencentAds tencentAds = TencentAds.getInstance();
            tencentAds.init(new ApiContextConfig());
            
            // 调用OAuth API刷新token
            Long clientId = Long.parseLong(tencentAdsConfig.getClientId());
            String clientSecret = tencentAdsConfig.getSecret();
            String grantType = "refresh_token";
            
            // 按照SDK的方法签名调用
            OauthTokenResponseData responseData = tencentAds.oauth()
                .oauthToken(clientId, clientSecret, grantType, auth.getRefreshToken(), null, null, null);
            
            if (responseData == null) {
                throw new Exception("刷新token响应为空");
            }
            
            // 更新授权信息
            return updateTokenData(auth, responseData);
            
        } catch (TencentAdsResponseException e) {
            log.error("刷新访问令牌失败 - API响应错误", e);
            // 刷新失败，标记为失效
            auth.setStatus(0);
            auth.setUpdateTime(LocalDateTime.now());
            updateById(auth);
            throw new Exception("刷新访问令牌失败：" + e.getMessage());
        } catch (TencentAdsSDKException e) {
            log.error("刷新访问令牌失败 - SDK错误", e);
            auth.setStatus(0);
            auth.setUpdateTime(LocalDateTime.now());
            updateById(auth);
            throw new Exception("刷新访问令牌失败：" + e.getMessage());
        }
    }

    /**
     * 保存token数据（新授权）
     */
    private TencentAdsAuth saveTokenData(OauthTokenResponseData responseData, String state) {
        String accessToken = responseData.getAccessToken();
        String refreshToken = responseData.getRefreshToken();
        Long accessTokenExpiresIn = responseData.getAccessTokenExpiresIn();
        Long refreshTokenExpiresIn = responseData.getRefreshTokenExpiresIn();

        //权限信息，当 grant_type=refresh_token 时不返回
        AuthorizerStruct authorizerInfo = responseData.getAuthorizerInfo();
         
        // 保存或更新授权信息
        TencentAdsAuth auth = new TencentAdsAuth();
        auth.setState(state);
        if (authorizerInfo != null) {
            Long accountId = authorizerInfo.getAccountId();
            auth.setAccountId(accountId);
            
            // 保存授权者结构体中的其他字段
            auth.setAccountUin(authorizerInfo.getAccountUin());
            auth.setWechatAccountId(authorizerInfo.getWechatAccountId());
            
            // 保存scope列表（转换为JSON字符串）
            if (authorizerInfo.getScopeList() != null) {
                auth.setScopeList(String.join(",", authorizerInfo.getScopeList()));
            }
            
            // 保存枚举类型字段（转换为字符串）
            if (authorizerInfo.getAccountRoleType() != null) {
                auth.setAccountRoleType(authorizerInfo.getAccountRoleType().toString());
            }
            if (authorizerInfo.getAccountType() != null) {
                auth.setAccountType(authorizerInfo.getAccountType().toString());
            }
            if (authorizerInfo.getRoleType() != null) {
                auth.setRoleType(authorizerInfo.getRoleType().toString());
            }
        }
        auth.setAccessToken(accessToken);
        auth.setRefreshToken(refreshToken);
        auth.setAccessTokenExpiresAt(LocalDateTime.now().plusSeconds(accessTokenExpiresIn));
        auth.setRefreshTokenExpiresAt(LocalDateTime.now().plusSeconds(refreshTokenExpiresIn));
        auth.setScope(""); 
        auth.setStatus(1);
        auth.setCreateTime(LocalDateTime.now());
        auth.setUpdateTime(LocalDateTime.now());
        
        // select by accountId
        LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TencentAdsAuth::getAccountId, auth.getAccountId())
                .eq(TencentAdsAuth::getDeleted, 0)
                .orderByDesc(TencentAdsAuth::getId)
                .last("LIMIT 1");
        TencentAdsAuth existAuth = getOne(wrapper);
        if (existAuth != null) {
            auth.setId(existAuth.getId());
            updateById(auth);
        } else {
            save(auth);
        }
        return auth;
    }

    /**
     * 更新token数据（刷新token）
     */
    @CacheEvict(value = "tencentAdsAuth", key = "#auth.accountId")
    private TencentAdsAuth updateTokenData(TencentAdsAuth auth, OauthTokenResponseData responseData) {
        String accessToken = responseData.getAccessToken();
        String refreshToken = responseData.getRefreshToken();
        Long accessTokenExpiresIn = responseData.getAccessTokenExpiresIn();
        Long refreshTokenExpiresIn = responseData.getRefreshTokenExpiresIn();
        
        // 更新授权信息
        auth.setAccessToken(accessToken);
        auth.setRefreshToken(refreshToken);
        auth.setAccessTokenExpiresAt(LocalDateTime.now().plusSeconds(accessTokenExpiresIn));
        auth.setRefreshTokenExpiresAt(LocalDateTime.now().plusSeconds(refreshTokenExpiresIn));
        auth.setStatus(1);
        auth.setUpdateTime(LocalDateTime.now());
        
        updateById(auth);
        
        log.info("刷新访问令牌成功，账户ID：{}", auth.getAccountId());
        return auth;
    }

    /**
     * 根据账户ID查询授权信息
     * 
     * @param accountId 账户ID
    /**
     * 根据账户ID查询授权信息（带缓存，1分钟过期）
     * 
     * @param accountId 账户ID
     * @return 授权信息
     */
    @Cacheable(value = "tencentAdsAuth", key = "#accountId", unless = "#result == null", condition = "#accountId != null")
    public TencentAdsAuth getByAccountId(Long accountId) {
        LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TencentAdsAuth::getAccountId, accountId)
                .eq(TencentAdsAuth::getDeleted, 0)
                .orderByDesc(TencentAdsAuth::getId)
                .last("LIMIT 1");
        return getOne(wrapper);
    }
    
    /**
     * 获取所有有效的授权信息
     * 
     * @return 授权信息列表
     */
    public List<TencentAdsAuth> listValidAuths(String jobNumber) {
        LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TencentAdsAuth::getState, jobNumber)
                .eq(TencentAdsAuth::getStatus, 1)
                .eq(TencentAdsAuth::getDeleted, 0);
        return list(wrapper);
    }

    /**
     * 获取即将过期的授权信息（30分钟内过期）
     * 
     * @return 授权信息列表
     */
    public List<TencentAdsAuth> listExpiringAuths() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime threshold = now.plusMinutes(30);
        
        LambdaQueryWrapper<TencentAdsAuth> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TencentAdsAuth::getStatus, 1)
                .eq(TencentAdsAuth::getDeleted, 0)
                .le(TencentAdsAuth::getAccessTokenExpiresAt, threshold)
                .gt(TencentAdsAuth::getRefreshTokenExpiresAt, now);
        
        return list(wrapper);
    }
}
