package org.jeecg.modules.adweb.marketing.googleads.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import jakarta.annotation.PostConstruct;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.jeecg.modules.adweb.common.databridge.OpenAPIRequest;
import org.jeecg.modules.adweb.common.databridge.OpenAPIResponse;
import org.jeecg.modules.adweb.common.util.AdwebRedisUtil;
import org.jeecg.modules.adweb.common.util.DateUtil;
import org.jeecg.modules.adweb.common.util.RestTemplateUtil;
import org.jeecg.modules.adweb.marketing.googleads.dto.CampaignStatsDTO;
import org.jeecg.modules.adweb.marketing.googleads.dto.CustomerStatsDTO;
import org.jeecg.modules.adweb.marketing.googleads.dto.MetricsDTO;
import org.jeecg.modules.adweb.marketing.googleads.dto.ReportRequestDTO;
import org.jeecg.modules.adweb.marketing.googleads.entity.GoogleAds;
import org.jeecg.modules.adweb.marketing.googleads.vo.*;
import org.jeecg.modules.redis.CacheConfig;
import org.jeecg.modules.redis.TTLCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Google Ads数据拉取 - http://data-bridge.v3.adwebcloud.com:9002/swagger-ui/index.html
 *
 * @author wfansh
 */
@Slf4j
@Service
public class GoogleAdsReportService {

    private static final String GOOGLE_ADS_CUSTOMER_STATS_API_PATH =
            "/api/google/ads/customerStats";
    private static final String GOOGLE_ADS_DAILY_STATS_API_PATH = "/api/google/ads/dailyStats";
    private static final String GOOGLE_ADS_CAMPAIGN_STATS_API_PATH =
            "/api/google/ads/campaignStats";
    private static final String GOOGLE_ADS_KEYWORD_STATS_API_PATH = "/api/google/ads/keywordStats";
    private static final String GOOGLE_ADS_PLACEMENT_STATS_API_PATH =
            "/api/google/ads/placementStats";
    private static final String GOOGLE_ADS_COUNTRY_STATS_API_PATH = "/api/google/ads/countryStats";

    @Value("${data-bridge.api.host}")
    private String dataBridgeApiHost;

    @Value("${data-bridge.api.token}")
    private String dataBridgeApiToken;

    @Autowired private IGoogleAdsService googleAdsService;

    @Autowired private AdwebRedisUtil adwebRedisUtil;

    private RestTemplate restTemplate;

    @PostConstruct
    private void init() {
        this.restTemplate = RestTemplateUtil.getRestTemplate(60, 60, dataBridgeApiToken);
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getCustomerStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public CustomerStatsVO getCustomerStats(String siteCode) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return new CustomerStatsVO(); // @Cacheable方法不允许返回null，返回CustomerStatsVO默认值
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, null, null, 1); // limit值无效

        // 2. 请求API接口
        OpenAPIResponse<CustomerStatsDTO> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_CUSTOMER_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return CustomerStatsVO.fromCustomerStatsDTO(openAPIResponse.getData());
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getDailyStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public List<DailyStatsVO> getDailyStats(String siteCode, Date start, Date end) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        final int limit = 365; // 最多365天

        // 0. 起止日期为空时（如 全部时间），返回最近limit天数据
        // - buildAPIRequest()方法设置无限早start，返回最早limit天数据，不符合预期
        if (Objects.isNull(end)) {
            end = new Date();
        }
        if (Objects.isNull(start)) {
            start = DateUtil.addDays(end, 1 - limit);
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, start, end, limit);

        // 2. 请求API接口
        OpenAPIResponse<Map<String, MetricsDTO>> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_DAILY_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return openAPIResponse.getData().entrySet().stream()
                .map(DailyStatsVO::fromMetricsDTO)
                .collect(Collectors.toList());
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getCampaignStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public List<CampaignStatsVO> getCampaignStats(String siteCode, Date start, Date end) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, start, end, 50); // 返回top 50

        // 2. 请求API接口
        OpenAPIResponse<List<CampaignStatsDTO>> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_CAMPAIGN_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return openAPIResponse.getData().stream()
                .map(CampaignStatsVO::fromCampaignStatsDTO)
                .collect(Collectors.toList());
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getKeywordStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public List<KeywordStatsVO> getKeywordStats(String siteCode, Date start, Date end) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, start, end, 50); // 返回top 50

        // 2. 请求API接口
        OpenAPIResponse<Map<String, MetricsDTO>> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_KEYWORD_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return openAPIResponse.getData().entrySet().stream()
                .map(KeywordStatsVO::fromMetricsDTO)
                .collect(Collectors.toList());
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getPlacementStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public List<PlacementStatsVO> getPlacementStats(String siteCode, Date start, Date end) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, start, end, 50); // 返回top 50

        // 2. 请求API接口
        OpenAPIResponse<Map<String, MetricsDTO>> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_PLACEMENT_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return openAPIResponse.getData().entrySet().stream()
                .map(PlacementStatsVO::fromMetricsDTO)
                .collect(Collectors.toList());
    }

    @Cacheable(
            cacheManager = CacheConfig.TTL_CACHE_MANAGER,
            cacheNames =
                    "googleads:getCountryStats"
                            + TTLCacheManager.TTL_SPLITTER
                            + 60 * 5) // Redis TTL为5分钟
    public List<CountryStatsVO> getCountryStats(String siteCode, Date start, Date end) {
        GoogleAds googleAds = this.getAdsAccount(siteCode);
        if (StringUtils.isBlank(googleAds.getCustomerId())) {
            log.info("Google Ads帐号未配置, siteCode = {}", siteCode);
            return Collections.EMPTY_LIST;
        }

        // 1. 创建API request
        OpenAPIRequest<ReportRequestDTO> openAPIRequest =
                this.buildOpenAPIRequest(googleAds, start, end, 50); // 返回top 50

        // 2. 请求API接口
        OpenAPIResponse<Map<String, MetricsDTO>> openAPIResponse =
                RestTemplateUtil.postForObject(
                        restTemplate,
                        dataBridgeApiHost + GOOGLE_ADS_COUNTRY_STATS_API_PATH,
                        openAPIRequest,
                        new ParameterizedTypeReference<>() {});

        return openAPIResponse.getData().entrySet().stream()
                .map(CountryStatsVO::fromMetricsDTO)
                .collect(Collectors.toList());
    }

    /**
     * 获取{@link GoogleAds}帐号信息 - 放入Redis缓存，避免同一页面多个HTTP请求频繁DB查询
     *
     * <p>使用AdwebRedisUtil操作 - @Cacheable注解无法嵌套使用
     */
    private synchronized GoogleAds getAdsAccount(String siteCode) {
        final String redisKey = String.format("%s:account:%s", "googleads", siteCode);

        GoogleAds googleAds = (GoogleAds) adwebRedisUtil.get(redisKey);
        if (Objects.nonNull(googleAds)) {
            return googleAds;
        }

        // DB查询
        googleAds =
                googleAdsService.getOne(
                        new LambdaQueryWrapper<GoogleAds>().eq(GoogleAds::getSiteCode, siteCode),
                        false);
        googleAds = Objects.nonNull(googleAds) ? googleAds : new GoogleAds(); // Redis存储对象不允许为空
        adwebRedisUtil.set(redisKey, googleAds, 30); // Redis TTL为30秒

        return googleAds;
    }

    private OpenAPIRequest<ReportRequestDTO> buildOpenAPIRequest(
            GoogleAds googleAds, Date start, Date end, int limit) {
        ReportRequestDTO reportRequest = new ReportRequestDTO();
        reportRequest.setCustomerId(googleAds.getCustomerId());
        reportRequest.setRefreshToken(googleAds.getRefreshToken());
        reportRequest.setStartDate(
                DateUtil.formatDate(
                        Optional.ofNullable(start)
                                // 如果start为空，设置为无限早时间
                                .orElse(DateUtil.parseDate("2016-01-01", DateUtil.DATE_FORMAT)),
                        DateUtil.DATE_FORMAT));
        reportRequest.setEndDate(
                DateUtil.formatDate(
                        Optional.ofNullable(end).orElse(new Date()), DateUtil.DATE_FORMAT));
        reportRequest.setLimit(limit);

        OpenAPIRequest<ReportRequestDTO> openAPIRequest = new OpenAPIRequest<>();
        openAPIRequest.setRequestServer(this.getClass().getSimpleName());
        openAPIRequest.setRequestTime(System.currentTimeMillis());
        openAPIRequest.setData(reportRequest);
        return openAPIRequest;
    }
}
