package vip.xiaonuo.coldchain.modular.qp.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import vip.xiaonuo.coldchain.modular.qp.vo.QpAlertConfigVo;
import vip.xiaonuo.common.cache.CommonCacheOperator;

import java.util.*;

@Slf4j
@Component
@RequiredArgsConstructor
public class QpApi {

    private final CommonCacheOperator cacheOperator;
    private final QpConfig qpConfig;

    /**
     * redis accessToken key
     */
    public static final String QP_ACCESS_TOKEN_KEY = "qp:accessToken";

    /**
     * 采集指标名称
     */
    public static final List<String> QP_METRIC_NAMES = Arrays.asList("temperature", "humidity", "co2", "battery");

    /**
     * 获取accessToken
     */
    public String getAccessToken() {
        Object token = cacheOperator.get(QP_ACCESS_TOKEN_KEY);
        JSONObject tokenObj;
        if (token == null) {
            // 获取accessToken
            try (HttpResponse response = HttpUtil.createPost("https://oauth.cleargrass.com/oauth2/token")
                    .contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
                    .auth("Basic " + Base64.getEncoder().encodeToString((qpConfig.getAppKey() + ":" + qpConfig.getAppSecret()).getBytes()))
                    .form("grant_type", "client_credentials")
                    .form("scope", "device_full_access")
                    .execute()) {
                String body = response.body();
                log.info("获取青萍accessToken: {}", body);
                tokenObj = JSON.parseObject(body);
                cacheOperator.put(QP_ACCESS_TOKEN_KEY, body, tokenObj.getLong("expires_in"));
            }
        } else {
            tokenObj = JSON.parseObject(token.toString());
        }

        return tokenObj.getString("access_token");
    }


    /**
     * 查询报警配置
     *
     * @param mac 设置mac地址
     */
    public List<QpAlertConfigVo> getAlertConfig(String mac) {
        Assert.notBlank(mac, "mac不能为空");

        String accessToken = getAccessToken();
        try (HttpResponse response = HttpUtil.createGet("https://apis.cleargrass.com/v1/apis/devices/settings/alert?mac=" + mac)
                .bearerAuth(accessToken)
                .execute()) {
            String body = response.body();
            log.info("查询青萍报警配置: {}", body);
            JSONArray configs = JSON.parseObject(body).getJSONArray("alert_configs");
            if (CollUtil.isEmpty(configs)) {
                return Collections.emptyList();
            }
            return configs.toJavaList(QpAlertConfigVo.class);
        }
    }

    /**
     * 修改报警配置
     *
     * @param mac      mac地址
     * @param configVo 报警配置
     */
    public void updateAlertConfig(String mac, QpAlertConfigVo configVo) {
        Assert.notBlank(mac, "mac不能为空");
        Assert.notNull(configVo, "配置不能为空");

        JSONObject param = new JSONObject()
                .fluentPut("mac", mac)
                .fluentPut("alert_config", configVo)
                .fluentPut("timestamp", System.currentTimeMillis());

        String accessToken = getAccessToken();
        try (HttpResponse response = HttpUtil.createRequest(Method.PUT, "https://apis.cleargrass.com/v1/apis/devices/settings/alert")
                .bearerAuth(accessToken)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .body(param.toJSONString())
                .execute()) {
            boolean ok = response.isOk();
            log.info("修改青萍报警配置 {}", ok ? "成功" : "失败");
        }
    }

    /**
     * 添加单个报警配置
     *
     * @param mac      mac地址
     * @param configVo 报警配置
     */
    public void addAlertConfig(String mac, QpAlertConfigVo configVo) {
        Assert.notBlank(mac, "mac不能为空");
        Assert.notNull(configVo, "配置不能为空");
        log.info("添加青萍报警配置 mac={}, config={}", mac, configVo);

        //添加配置移除id
        JSONObject configObj = JSON.parseObject(JSON.toJSONString(configVo));
        configObj.remove("id");
        JSONObject param = new JSONObject()
                .fluentPut("mac", mac)
                .fluentPut("alert_config", configObj)
                .fluentPut("timestamp", System.currentTimeMillis());

        String accessToken = getAccessToken();
        try (HttpResponse response = HttpUtil.createPost("https://apis.cleargrass.com/v1/apis/devices/settings/alert")
                .bearerAuth(accessToken)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .body(param.toJSONString())
                .execute()) {
            boolean ok = response.isOk();
            log.info("添加青萍报警配置 {}", ok ? "成功" : "失败");
        }
    }

    /**
     * 删除报警配置
     *
     * @param mac       mac地址
     * @param configIds 配置id列表
     */
    public void deleteAlertConfig(String mac, List<Long> configIds) {
        Assert.notBlank(mac, "mac不能为空");
        if (CollUtil.isEmpty(configIds)) {
            log.info("青萍mac={} 删除配置为空", mac);
            return;
        }
        log.info("删除青萍mac={} 配置 configIds={}", mac, configIds);
        JSONObject param = new JSONObject()
                .fluentPut("mac", mac)
                .fluentPut("config_id", configIds)
                .fluentPut("timestamp", System.currentTimeMillis());

        String accessToken = getAccessToken();
        try (HttpResponse response = HttpUtil.createRequest(Method.DELETE, "https://apis.cleargrass.com/v1/apis/devices/settings/alert")
                .bearerAuth(accessToken)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .body(param.toJSONString())
                .execute()) {
            boolean ok = response.isOk();
            log.info("删除青萍报警配置 {}", ok ? "成功" : "失败");
        }
    }

    /**
     * 保存或更新报警配置
     *
     * @param mac       mac地址
     * @param configVos 报警配置
     */
    public void saveConfig(String mac, List<QpAlertConfigVo> configVos) {
        List<QpAlertConfigVo> configList = getAlertConfig(mac);
        log.info("更新青萍报警配置 mac={}, configs={}", mac, configList);
        if (CollUtil.isNotEmpty(configList)) {
            //删除温度、湿度、CO2报警配置
            List<Long> configIds = configList.stream().filter(x -> QP_METRIC_NAMES.contains(x.getMetric_name())).map(QpAlertConfigVo::getId).toList();
            deleteAlertConfig(mac, configIds);
        }

        for (QpAlertConfigVo configVo : configVos) {
            addAlertConfig(mac, configVo);

            //调用接口暂停一会
            try {
                Thread.sleep(200);
            } catch (InterruptedException ignored) {
            }
        }
    }

    /**
     * 获取设备列表
     */
    public JSONObject getDevices() {
        String accessToken = getAccessToken();
        try (HttpResponse response = HttpUtil.createGet("https://apis.cleargrass.com/v1/apis/devices?timestamp=" + System.currentTimeMillis())
                .bearerAuth(accessToken)
                .execute()) {
            String body = response.body();
            log.info("查询青萍设备列表: {}", body);
            return JSON.parseObject(body);
        }
    }

    /**
     * 获取设备数据
     *
     * @param mac mac地址
     * @return {"battery":{"value":70},"humidity":{"value":22.2},"pressure":{"value":103.29},"temperature":{"value":20.8},"timestamp":{"value":1574565267}}
     */
    public JSONObject getDeviceData(String mac) {
        Assert.notBlank(mac, "mac不能为空");
        JSONObject devices = getDevices();
        if (devices.getInteger("total") < 1) {
            return new JSONObject();
        }

        JSONArray deviceArray = devices.getJSONArray("devices");
        Optional<JSONObject> opt = deviceArray.stream()
                .map(x -> (JSONObject) x)
                .filter(x -> mac.equals(x.getJSONObject("info").getString("mac")))
                .findFirst();
        if (opt.isEmpty()) {
            return new JSONObject();
        }

        return opt.get().getJSONObject("data");
    }
}
