package com.iailab.sdk.auth.client;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iailab.sdk.auth.client.dto.ApiDataQueryDTO;
import com.iailab.sdk.auth.client.dto.TokenDTO;
import com.iailab.sdk.auth.config.SdkAutoConfiguration;
import com.iailab.sdk.auth.constants.SdkErrorCodeConstants;
import com.iailab.sdk.util.http.IailabHttpUtils;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.util.Assert;
import org.springframework.util.Base64Utils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.*;

import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception;

/**
 * @author Houzhongjian
 * @Description
 * @createTime 2025年02月18日
 */
public class IailabAuthClient {

    private static final RestTemplate restTemplate = new RestTemplate();


    /**
     * 平台地址
     */
    public static String BASE_URL = SdkAutoConfiguration.BASE_URL;

    /**
     * 租户编号
     */
    public static Long TENANT_ID = SdkAutoConfiguration.TENANT_ID;

    /**
     * 客户端信息
     */
    private static String CLIENT_ID = SdkAutoConfiguration.CLIENT_ID;
    private static String CLIENT_SECRET = SdkAutoConfiguration.CLIENT_SECRET;
    private static String USERNAME = SdkAutoConfiguration.USERNAME;
    private static String PASSWORD = SdkAutoConfiguration.PASSWORD;

    private static final String GRAND_TYPE = "password";

    private static final String SCOPE = "user.read user.write";

    private static final String CHARSET = "utf-8";

    private static final IailabAuthClient iailabAuthClient = new IailabAuthClient();

    public static IailabAuthClient getInstance() {
        return iailabAuthClient;
    }


    // 鉴权token
    public static String accessToken;
    // 刷新token
    public static String refreshToken;
    // 鉴权token过期时间
    public static Long expireTime;

    private static final String RESP_CODE = "code";
    private static final String RESP_DATA = "data";

    /**
     * 用户名密码方式获取平台token
     */
    public static synchronized TokenDTO authenticate() {
        System.out.println("登录获取平台token");
        // 1.1 构建请求头
        HttpHeaders headers = new HttpHeaders();
        addClientHeader(headers);
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        // 1.2 构建authenticate请求URL
        String authenticateUrl = BASE_URL + "/system/oauth2/token?"
                // 密码模式的参数
                + "&grant_type=" + GRAND_TYPE
                + "&username=" + USERNAME
                + "&password=" + PASSWORD
                + "&scope=" + SCOPE;
        // 2. 执行请求
        ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange(
                authenticateUrl,
                HttpMethod.POST,
                new HttpEntity<>(headers),
                new ParameterizedTypeReference<Map<String, Object>>() {
                });
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
        Map<String, Object> authMap = exchange.getBody();
        Object code = authMap.get("code");
        if(SdkErrorCodeConstants.AUTH_BAD_CREDENTIALS.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.AUTH_BAD_CREDENTIALS);
        } else if(SdkErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS);
        } else if(SdkErrorCodeConstants.OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.OAUTH2_CLIENT_REDIRECT_URI_NOT_MATCH);
        } else if(SdkErrorCodeConstants.OAUTH2_CLIENT_CLIENT_SECRET_ERROR.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.OAUTH2_CLIENT_CLIENT_SECRET_ERROR);
        } else if(SdkErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.OAUTH2_CLIENT_NOT_EXISTS);
        } else if(SdkErrorCodeConstants.OAUTH2_CLIENT_DISABLE.getCode().equals(code)) {
            throw exception(SdkErrorCodeConstants.OAUTH2_CLIENT_DISABLE);
        }
        accessToken = authMap.get("access_token").toString();
        refreshToken = authMap.get("refresh_token").toString();
        expireTime = Long.valueOf(authMap.get("expires_time").toString());
        return handleResponse(exchange);
    }

    public static synchronized TokenDTO refreshToken() throws Exception {
        System.out.println("刷新token");
        // 1.1 构建请求头
        HttpHeaders headers = new HttpHeaders();
        addClientHeader(headers);
        // 1.2 构建authenticate请求URL
        String authenticateUrl = BASE_URL + "/system/auth/client-refresh-token?refreshToken=" + refreshToken + "&clientId=" + CLIENT_ID;
        // 2. 执行请求
        ResponseEntity<Map<String, Object>> exchange = restTemplate.exchange(
                authenticateUrl,
                HttpMethod.POST,
                new HttpEntity<>(headers),
                new ParameterizedTypeReference<Map<String, Object>>() {
                });
        Assert.isTrue(exchange.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
        Map<String, Object> authMap = exchange.getBody();
        //刷新token过期,重新获取token
        if (!ObjectUtils.isEmpty(authMap.get("code"))) {
            Integer code = Integer.valueOf(authMap.get("code").toString());
            if (code == 401) {
                authenticate();
            } else {
                throw exception(SdkErrorCodeConstants.AUTH_REFRESH_TOKEN_ERROR);
            }
        } else {
            accessToken = authMap.get("access_token").toString();
            expireTime = Long.valueOf(authMap.get("expires_time").toString());
        }
        return handleResponse(exchange);
    }

    /**
     * 平台http请求封装
     * @param method
     * @param url
     * @param params
     * @return
     * @throws Exception
     */
    public static String doHttp(String method, String url, Map<String, Object> params) throws Exception {
        String response = null;
        String upperMethod = method.toUpperCase();
        if("GET".equals(upperMethod)) {
            response = IailabHttpUtils.doGet(url, params, CHARSET);
        } else if("POST".equals(upperMethod)) {
            ObjectMapper objectMapper = new ObjectMapper();
            response = IailabHttpUtils.doPost(url, objectMapper.writeValueAsString(params), CHARSET);
        }
        return response;
    }

    private static void addClientHeader(HttpHeaders headers) {
        // client 拼接,需要 BASE64 编码
        String client = CLIENT_ID + ":" + CLIENT_SECRET;
        client = Base64Utils.encodeToString(client.getBytes(StandardCharsets.UTF_8));
        headers.add("Authorization", "Basic " + client);
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("tenant-id", TENANT_ID.toString());
    }

    // 统一处理响应
    private static <T> TokenDTO handleResponse(ResponseEntity<T> response) {
        Assert.isTrue(response.getStatusCode().is2xxSuccessful(), "响应必须是 200 成功");
        System.out.println(response);
        TokenDTO authTokenDTO = new TokenDTO();
        Map<String, Object> authMap = (Map<String, Object>)response.getBody();
        authTokenDTO.setAccessToken(authMap.get("access_token").toString());
        authTokenDTO.setRefreshToken(authMap.get("refresh_token").toString());
        authTokenDTO.setExpiresTime(Long.valueOf(authMap.get("expires_time").toString()));
        return authTokenDTO;
    }

    /**
     * 查询计划chart (测试用)
     * @param apiDataQueryDTO
     * @return
     * @throws Exception
     */
    public Map<String, Object> queryPlanChart(ApiDataQueryDTO apiDataQueryDTO) throws Exception {
        Map<String, Object> data = new HashMap<>();
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonString = objectMapper.writeValueAsString(apiDataQueryDTO);
        String resp = IailabHttpUtils.doPost(BASE_URL + "/data/api/query-plans/chart", jsonString, "utf-8");
        if (StringUtils.isEmpty(resp)) {
            return data;
        }
        JSONObject jsonObject = JSON.parseObject(resp);
        data = jsonObject.getJSONObject(RESP_DATA).toJavaObject(Map.class);
        return data;
    }

    /**
     * 查询多个测点当前值
     *
     * @param pointNos
     * @return
     */
    public Map<String, Object> queryPointsRealValue(List<String> pointNos) throws Exception {
        Map<String, Object> data = new HashMap<>();
        String url = BASE_URL + "/data/api/query-points/real-value";
        String resp = IailabHttpUtils.doPost(url, JSON.toJSONString(pointNos), "UTF-8");
        if (StringUtils.isEmpty(resp)) {
            return data;
        }
        JSONObject jsonObject = JSON.parseObject(resp);
        data = jsonObject.getJSONObject(RESP_DATA).toJavaObject(Map.class);
        return data;
    }

}