/*
 * 文 件 名:  InDoorEnergyTemplate
 * 版    权:  华设设计集团股份有限公司
 * 描    述:  <描述>
 * 修 改 人:  lvwenbin
 * 修改时间:  2025/8/15
 * 跟踪单号:  <跟踪单号>
 * 修改单号:  <修改单号>
 * 修改内容:  <修改内容>
 */
package com.ruoyi.ems.core;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.retrofit.Retrofit2ConverterFactory;
import com.alibaba.fastjson2.JSON;
import com.huashe.common.exception.Assert;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.ems.config.BaCtlConfig;
import com.ruoyi.ems.model.CallData;
import com.ruoyi.ems.model.idenergy.CodesValReq;
import com.ruoyi.ems.model.idenergy.CodesValSetReq;
import com.ruoyi.ems.model.idenergy.Token;
import com.ruoyi.ems.retrofit.BaCtlEnergyApi;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import org.apache.commons.lang3.StringUtils;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;

import java.util.concurrent.TimeUnit;

/**
 * 室内能耗操作模板
 * <功能详细描述>
 *
 * @author lvwenbin
 * @version [版本号, 2025/8/15]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Slf4j
public class BaCtlEnergyTemplate extends BaseApiTemplate {
    /**
     * 访问地址
     */
    protected String url;

    /**
     * 调用代理
     */
    protected final BaCtlEnergyApi api;

    public BaCtlEnergyTemplate(String restUrl) {
        this.url = restUrl;

        OkHttpClient httpClient = getClient();
        Retrofit retrofit = new Retrofit.Builder().baseUrl(restUrl)
            .addConverterFactory(Retrofit2ConverterFactory.create()).client(httpClient).build();
        this.api = retrofit.create(BaCtlEnergyApi.class);
    }

    /**
     * 构造调用模板
     *
     * @param restUrl        服务地址 (http://ip:port)
     * @param connectTimeout 连接超时
     * @param readTimeout    读取超时
     * @param writeTimeout   写超时
     */
    public BaCtlEnergyTemplate(String restUrl, int connectTimeout, int readTimeout, int writeTimeout) {
        super.connectTimeout = connectTimeout;
        super.readTimeout = readTimeout;
        super.writeTimeout = writeTimeout;
        this.url = restUrl;

        OkHttpClient httpClient = getClient();
        Retrofit retrofit = new Retrofit.Builder().baseUrl(restUrl)
            .addConverterFactory(Retrofit2ConverterFactory.create()).client(httpClient).build();
        api = retrofit.create(BaCtlEnergyApi.class);
    }

    public synchronized String getToken() {
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        String accessToken = redisService.getCacheObject("INDOOR_ENERGY_ACCESS_TOKEN");

        if (StringUtils.isEmpty(accessToken)) {
            accessToken = getNewToken();
        }

        return accessToken;
    }

    public String getNewToken() {
        RedisService redisService = SpringUtils.getBean(RedisService.class);

        BaCtlConfig config = SpringUtils.getBean(BaCtlConfig.class);
        Token token = getAuthToken(config.getAppId());
        log.info("getAuthToken response:{}", JSONObject.toJSONString(token));
        redisService.setCacheObject("INDOOR_ENERGY_ACCESS_TOKEN", token.getAccessToken(), 5 * 60L, TimeUnit.SECONDS);
        redisService.setCacheObject("INDOOR_ENERGY_REFRESH_TOKEN", token.getRefreshToken());

        return token.getAccessToken();
    }

    public synchronized String refreshToken() {
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        String refreshToken = redisService.getCacheObject("INDOOR_ENERGY_REFRESH_TOKEN");
        BaCtlConfig config = SpringUtils.getBean(BaCtlConfig.class);
        Token token = StringUtils.isEmpty(refreshToken) ?
            getAuthToken(config.getAppId()) :
            refreshToken(config.getAppId(), refreshToken);
        redisService.setCacheObject("INDOOR_ENERGY_ACCESS_TOKEN", token.getAccessToken(), 5 * 60L, TimeUnit.SECONDS);
        redisService.setCacheObject("INDOOR_ENERGY_REFRESH_TOKEN", token.getRefreshToken(), 60 * 60L, TimeUnit.SECONDS);

        return token.getAccessToken();
    }

    /**
     * 获取Token
     *
     * @param appId appId
     * @return 响应
     */
    public Token getAuthToken(String appId) {
        Token token = null;

        try {
            // 执行调用
            Call<String> call = api.getAccessToken(appId, "authorization");
            Response<String> response = call.execute();
            log.debug("getAccessToken response:{}", response);

            Assert.notEmpty(response.body(), -1, "获取Token失败");
            token = JSONObject.parseObject(response.body(), Token.class);
            Assert.notNull(token, -1, "转换Token失败:\n" + response.body());
        }
        catch (Exception e) {
            log.error("getAccessToken fail!", e);
        }

        return token;
    }

    /**
     * 刷新token
     *
     * @param appId        appId
     * @param refreshToken 缓存token
     * @return token
     */
    public Token refreshToken(String appId, String refreshToken) {
        Token token = null;

        try {
            // 执行调用
            Call<String> call = api.refreshToken(appId, "refresh", refreshToken);
            Response<String> response = call.execute();
            log.debug("refreshToken response:{}", response);

            Assert.notEmpty(response.body(), -1, "获取Token失败");
            token = JSONObject.parseObject(response.body(), Token.class);
            Assert.notNull(token, -1, "转换Token失败:\n" + response.body());
        }
        catch (Exception e) {
            log.error("refreshToken fail!", e);
        }

        return token;
    }

    /**
     * 获取实时数据
     *
     * @param req 请求
     * @return 实时数据列表
     */
    public CallData<String> getCodesVal(CodesValReq req) {
        CallData.Builder<String> builder = CallData.builder();

        try {
            String token = getToken();
            BaCtlConfig config = SpringUtils.getBean(BaCtlConfig.class);
            // 执行调用
            builder.addCallParam("appId", config.getAppId()).addCallParam("token", token).addCallParam("req", JSONObject.toJSONString(req));
            Call<String> call = api.getCodesVal(config.getAppId(), token, req);
            Response<String> response = call.execute();
            log.debug("getCodesVal response:{}", response);

            JSONObject resJson = JSONObject.parseObject(response.body());

            if (resJson.getInteger("code") == 200) {
                builder.setCallStatus(0).setResPayload(response.body());
            }
            else if (resJson.getInteger("code") == 302) {
                token = getNewToken();
                // 执行调用
                call = api.getCodesVal(config.getAppId(), token, req);
                response = call.execute();
                log.debug("getCodesVal response:{}", response);
                builder.setCallStatus(0).setResPayload(response.body());
            }
        }
        catch (Exception e) {
            builder.setCallStatus(1);
            log.error("getCodesVal fail!", e);
        }

        return builder.build();
    }

    /**
     * 设置下发
     *
     * @param req 请求
     * @return 能耗数据列表
     */
    public CallData<String> setCodesVal(CodesValSetReq req) {
        CallData.Builder<String> builder = CallData.builder();

        try {
            String token = getToken();
            BaCtlConfig config = SpringUtils.getBean(BaCtlConfig.class);
            // 执行调用
            builder.addCallParam("appId", config.getAppId()).addCallParam("token", token).addCallParam("req", JSON.toJSONString(req));
            Call<String> call = api.setCodesVal(config.getAppId(), token, req);
            Response<String> response = call.execute();
            log.debug("setCodesVal response:{}", response);

            JSONObject resJson = JSONObject.parseObject(response.body());

            if (resJson.getInteger("code") == 200) {
                builder.setCallStatus(0).setResPayload(response.body());
            }
            else if (resJson.getInteger("code") == 302) {
                token = getNewToken();
                // 执行调用
                call = api.setCodesVal(config.getAppId(), token, req);
                response = call.execute();
                log.debug("setCodesVal response:{}", response);
                builder.setCallStatus(0).setResPayload(response.body());
            }
        }
        catch (Exception e) {
            builder.setCallStatus(1);
            log.error("setCodesVal fail!", e);
        }

        return builder.build();
    }
}