package com.kym.service.platform.impl;

import com.alibaba.fastjson2.JSONObject;
import com.kym.common.annotation.ConnectorID;
import com.kym.common.annotation.DynamicCache;
import com.kym.common.annotation.PlatformConvert;
import com.kym.common.annotation.PlatformName;
import com.kym.common.cache.PlatformCache;
import com.kym.common.constant.ResponseEnum;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.PlatformAesUtil;
import com.kym.common.utils.PlatformConvertUtil;
import com.kym.entity.common.RedisKeys;
import com.kym.entity.platform.PlatformRespQueryToken;
import com.kym.entity.platform.response.PlatformResponse;
import com.kym.service.platform.PlatformApi;
import com.kym.service.platform.PlatformApiService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import static com.kym.common.utils.PlatformAesUtil.buildPlatformParams;

/**
 * @author skyline
 * 互联互通平台接口服务
 */
@Service
@Slf4j
public class PlatformApiServiceImpl implements PlatformApiService {

    public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
    static OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(5, TimeUnit.SECONDS)      // 连接超时 5s
            .readTimeout(10, TimeUnit.SECONDS)        // 读取超时 10s
            .writeTimeout(10, TimeUnit.SECONDS)       // 写入超时 10s
            .connectionPool(new ConnectionPool(
                    10,                              // 最大空闲连接数
                    5,                               // 连接存活时间
                    TimeUnit.MINUTES
            ))
            .retryOnConnectionFailure(true)           // 启用重试
            .build();
    private final RedisTemplate<String, String> redisTemplate;

    public PlatformApiServiceImpl(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public static <T> T parse(String json, Class<T> clz) {
        return JSONObject.parseObject(json, clz);
    }

    private static String synchronizedCall(Request request) {
        try (Response response = HTTP_CLIENT.newCall(request).execute()) {
            return response.body().string();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 互联互通POST请求专属方法
     *
     * @param platformName
     * @param url
     * @param params
     * @return
     */
    @Override
    public PlatformResponse platformPost(String platformName, String url, String params) {
        // token获取
        var token = queryPlatformToken(platformName);
        Headers headers = Headers.of("Authorization", "Bearer ".concat(token));
        RequestBody requestBody = RequestBody.create(params, JSON);
        Request request = new Request.Builder()
                .headers(headers)
                .post(requestBody)
                .url(url)
                .build();
        var response = parse(synchronizedCall(request), PlatformResponse.class);
        CommUtil.asserts(null != response, "互联互通平台:" + platformName + "接口异常");

        if (0 == response.getRet()) {
            return response;
        } else {
            log.error("：url:{}， params:{}，token:{}，返回信息：{}", url, params, token, response);
            if (4002 == response.getRet()) {
                // 如果返回Ret=4002，token错误的情况下，删除redis中的token,如果是预约订单则将此订单设置为延迟启动
                redisTemplate.delete(RedisKeys.PLATFORM_QUERY_TOKEN + platformName);
                throw new BusinessException(ResponseEnum.PLATFORM_QUERY_TOKEN_EXCEPTION);
            }
            throw new BusinessException(ResponseEnum.PLATFORM_API_EXCEPTION);
        }
    }

    /**
     * 解析平台返回数据为JSONObject
     *
     * @param response
     * @param platformName
     * @return
     */
    JSONObject parsePlatformResponseData(PlatformResponse response, String platformName) {
        var platform = PlatformCache.INSTANCE.getPlatformByName(platformName);
        return JSONObject.parseObject(PlatformAesUtil.decrypt(platform.getDataSecret(), platform.getDataSecretIv(), response.getData()));
    }


    /**
     * 查询平台互联互通token
     *
     * @param platformName
     * @return
     */
    @Override
    public String queryPlatformToken(String platformName) {
        log.info("查询互联互通平台{}token", platformName);
        var token = redisTemplate.opsForValue().get(RedisKeys.PLATFORM_QUERY_TOKEN + platformName);
        if (CommUtil.isNotEmptyAndNull(token)) {
            // 不同平台不同key
            log.debug("从缓存中查询到token：{}，ttl:{}", token, redisTemplate.getExpire(RedisKeys.PLATFORM_QUERY_TOKEN + platformName));
            return token;
        }
        var platform = PlatformCache.INSTANCE.getPlatformByName(platformName);
        var data = """
                {
                    "OperatorID":"%s",
                    "OperatorSecret":"%s"
                }
                """.formatted(platform.getUserOperatorId(), platform.getOperatorSecret());

        var requestParams = buildPlatformParams(platformName, data);

        var response = platformGetToken(PlatformApi.PLATFORM_QUERY_TOKEN.getApi(platformName), requestParams);

        if (response != null && 0 == response.getRet()) {
            // 解密Data获取token
            var jsonObject = parsePlatformResponseData(response, platformName);
            var platformRespQueryToken = parse(jsonObject.toJSONString(), PlatformRespQueryToken.class);
            log.debug("{}接口AccessToken:{}", platformName, platformRespQueryToken.toString());
            // 缓存token不同平台不同key,有效期7天(我们这里每次请求en+获取token的有效期并不是从7天开始，有效期是在en+的剩余有效时间)
            redisTemplate.opsForValue().set(RedisKeys.PLATFORM_QUERY_TOKEN + platformName,
                    platformRespQueryToken.getAccessToken(), platformRespQueryToken.getTokenAvailableTime(), TimeUnit.SECONDS);
            return platformRespQueryToken.getAccessToken();
        } else {
            // 记录错误码，返回错误信息
            log.error("{}接口错误:接口名{}：返回信息：{}", platformName, "query_token", response);
            throw new BusinessException(ResponseEnum.PLATFORM_QUERY_TOKEN_ERROR);
        }
    }

    public PlatformResponse platformGetToken(String url, String params) {
        // token获取
        RequestBody requestBody = RequestBody.create(params, JSON);
        Request request = new Request.Builder()
                .post(requestBody)
                .url(url)
                .build();
        var response = parse(synchronizedCall(request), PlatformResponse.class);

        if (response != null && 0 == response.getRet()) {
            return response;
        } else {
            log.error("互联互通接口数据异常：url:{}， params:{}，返回信息：{}", url, params, response);
            throw new BusinessException(ResponseEnum.PLATFORM_API_EXCEPTION);
        }
    }

    /**
     * 请求设备认证
     *
     * @param platformName 互联互通平台名称
     * @param connectorId  充电设备接口编码
     * @param equipAuthSeq 格式：运营商ID+唯一编码 27字符
     * @return
     */
    @PlatformConvert
    @Override
    public JSONObject queryEquipAuth(@PlatformName String platformName, @ConnectorID String connectorId, String equipAuthSeq) {
        var param = """
                {
                    "EquipAuthSeq":"%s",
                    "ConnectorID":"%s"
                }
                """.formatted(equipAuthSeq, connectorId);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_EQUIP_AUTH.getApi(platformName), buildPlatformParams(platformName, param));

        return parsePlatformResponseData(response, platformName);
    }


    /**
     * 查询业务策略信息
     *
     * @param platformName 互联互通平台名称
     * @param equipBizSeq  业务策略查询流水号 格式：运营商ID+唯一编码 27字符
     * @param connectorId  充电设备接口编码
     * @return
     */
    @Override
    @DynamicCache(spel = "#connectorId")
    @PlatformConvert
    public JSONObject queryEquipBusinessPolicy(@PlatformName String platformName, String equipBizSeq, @ConnectorID String connectorId) {
        var param = """
                {
                    "EquipBizSeq":"%s",
                    "ConnectorID":"%s"
                }
                """.formatted(equipBizSeq, connectorId);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_EQUIP_BUSINESS_POLICY.getApi(platformName), buildPlatformParams(platformName, param));
        return parsePlatformResponseData(response, platformName);
    }

    /**
     * 请求EN+启动充电
     *
     * @param platformName
     * @param startChargeSeq
     * @param connectorId
     * @param qrCode
     * @param amount
     * @return
     */
    @Override
    @PlatformConvert
    public JSONObject queryStartCharge(@PlatformName String platformName, String startChargeSeq, @ConnectorID String connectorId, String qrCode, Integer amount) {
        var param = """
                {
                    "StartChargeSeq":"%s",
                    "ConnectorID":"%s",
                    "QRCode":"%s",
                }
                """.formatted(startChargeSeq, connectorId, qrCode);
        var completeParams = PlatformConvertUtil.queryStartChargeParamsConvert(platformName, param, amount);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_START_CHARGE.getApi(platformName), buildPlatformParams(platformName, completeParams));
        return parsePlatformResponseData(response, platformName);
    }

    /**
     * 查询充电状态
     *
     * @param platformName
     * @param startChargeSeq
     * @return
     */
    @Override
    public JSONObject queryEquipChargeStatus(String platformName, String startChargeSeq) {
        var param = """
                {
                    "StartChargeSeq":"%s"
                }
                """.formatted(startChargeSeq);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_EQUIP_CHARGE_STATUS.getApi(platformName), buildPlatformParams(platformName, param));
        return parsePlatformResponseData(response, platformName);
    }

    /**
     * 与en+开发约定使用该接口更新正在充电中的订单的金额信息，解决充电过程中用户充值续充场景 (注意：朗新不支持该功能)
     *
     * @param platformName
     * @param startChargeSeq
     * @param amount         【EN+互联互通扩展参数】
     * @return
     */
    @Override
    public JSONObject updateBalanceByQueryEquipChargeStatus(String platformName, String startChargeSeq, int amount) {
        var param = """
                {
                    "StartChargeSeq":"%s",
                    "amount":%d
                }
                """.formatted(startChargeSeq, amount);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_EQUIP_CHARGE_STATUS.getApi(platformName), buildPlatformParams(platformName, param));
        return parsePlatformResponseData(response, platformName);
    }

    /**
     * 请求停止充电
     *
     * @param platformName
     * @param startChargeSeq
     * @param connectorId
     * @return
     */
    @Override
    @PlatformConvert
    public JSONObject queryStopCharge(@PlatformName String platformName, String startChargeSeq, @ConnectorID String connectorId) {
        var param = """
                {
                    "StartChargeSeq":"%s",
                    "ConnectorID":"%s"
                }
                """.formatted(startChargeSeq, connectorId);
        var response = platformPost(platformName, PlatformApi.PLATFORM_QUERY_STOP_CHARGE.getApi(platformName), buildPlatformParams(platformName, param));
        return parsePlatformResponseData(response, platformName);
    }

}
