package com.fs.newAdv.integration.client;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fs.newAdv.domain.PromotionAccount;
import com.fs.newAdv.enums.AdvertiserTypeEnum;
import com.fs.newAdv.integration.factory.AdvertiserHandlerFactory;
import com.fs.newAdv.service.IApiCallLogService;
import com.fs.newAdv.service.IPromotionAccountService;
import com.fs.newAdv.vo.AccessTokenVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;
import java.util.Map;
import java.util.function.Function;

@Slf4j
public abstract class AbstractApiClient implements IApiClient {

    @Autowired
    private IApiCallLogService apiCallLogService;
    @Autowired
    private IPromotionAccountService promotionAccountService;
    @Autowired
    private AdvertiserHandlerFactory advertiserHandlerFactory;

    /**
     * 构建回传接口上下文信息 (traceId, userId等)
     */
    protected abstract Map<String, Object> buildConversionParams(Map<String, Object> conversionData);

    /**
     * 统一封装API调用日志和异常处理逻辑
     */
    protected boolean executeWithLog(AdvertiserTypeEnum advertiserType, String apiUrl, Map<String, Object> params, ApiCall action) {
        long start = System.currentTimeMillis();
        boolean callStatus = false;
        JSONObject jsonObject = null;
        try {
            log.info("[{}] 调用开始, 参数: {}", apiUrl, params);
            HttpResponse result = action.call();
            jsonObject = JSONUtil.parseObj(result.body());
            // 通用状态
            Integer code = jsonObject.getInt("code");
            // 百度状态
            Integer status = -1;
            JSONObject header = jsonObject.getJSONObject("header");
            if (ObjectUtil.isNotEmpty(header)) {
                status = header.getInt("status");
            }
            if ((ObjectUtil.isNotEmpty(code) && (code == 0 || code == 200 ))
            || (ObjectUtil.isNotEmpty(status) && status == 0)
            ) {
                callStatus = true;
            }
            log.info("[{}] 调用成功, 耗时: {} ms, 返回结果: {}", apiUrl, System.currentTimeMillis() - start, jsonObject);
        } catch (Exception e) {
            log.error("[{}] 调用失败, 耗时: {} ms, 错误信息: {}", apiUrl, System.currentTimeMillis() - start, e.getMessage(), e);
        }
        saveApiCallLog(advertiserType, apiUrl, params, jsonObject != null ? JSONUtil.toJsonStr(jsonObject) : null, callStatus, start);
        return callStatus;
    }

    /**
     * 保存日志到数据库或日志系统
     */
    protected void saveApiCallLog(AdvertiserTypeEnum advertiserType, String apiUrl, Map<String, Object> params
            , String response, boolean callStatus, long start) {
        apiCallLogService.saveApiCallLog(advertiserType.getCode(), apiUrl, JSONUtil.toJsonStr(params), response, callStatus, start);
    }


    @FunctionalInterface
    protected interface ApiCall {
        HttpResponse call() throws Exception;
    }

    protected String getAccessToken(Long promotionAccountId) {
        PromotionAccount byId = promotionAccountService.getById(promotionAccountId);
        // 判断token是否过期 提前1小时刷新
        if (ObjectUtil.isEmpty(byId.getExpireTime()) || byId.getExpireTime().isBefore(LocalDateTime.now().plusHours(1))) {
            // 获取请求参数
            IApiClient apiClient = advertiserHandlerFactory.getApiClient(AdvertiserTypeEnum.getByCode(byId.getAdvertiserId()));
            IAccessTokenClient tokenClient = (IAccessTokenClient) apiClient;
            AccessTokenVo accessTokenVo = tokenClient.refreshAccessToken(byId);
            byId.setAccessToken(accessTokenVo.getAccessToken());
            byId.setRefreshToken(accessTokenVo.getRefreshToken());
            byId.setExpireTime(accessTokenVo.getExpireTime());
            byId.setUpdateTime(LocalDateTime.now());
            promotionAccountService.updateById(byId);
            return accessTokenVo.getAccessToken();
        }
        return byId.getAccessToken();
    }

    protected <R> R executeToken(Function<JSONObject, R> mapper, ApiCall action) {
        HttpResponse execute = null;
        try {
            execute = action.call();
        } catch (Exception e) {
            log.error("广告token调用失败", e);
        }
        String body = execute.body();
        log.info("广告 token 接口响应: {}", body);
        JSONObject res = new JSONObject(body);
        int code = res.getInt("code");
        if (code == 0 || code == 200) {
            // 不同广告商返回的字段可能不一样 兼容
            return mapper.apply(res);
        }
        return null;
    }
}
