package com.kingdee.eas.custom.integration.sng.service;

import com.huawei.shade.com.alibaba.fastjson.JSONObject;
import com.kingdee.eas.cp.eip.sso.util.PropertiesUtil;
import com.kingdee.eas.custom.integration.base.exception.CommonErrorCodes;
import com.kingdee.eas.custom.integration.base.exception.IntegrationException;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description SNG接口服务
 * @Date 2026/4/9 19:21
 * @Created by HeYuan
 */
@Service
public class SNGApiService {
    private static final Logger logger = LoggerFactory.getLogger(SNGApiService.class);

    /**
     * OkHttpClient实例
     */
    private static final OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    /**
     * 获取Token
     *
     * @param appId 应用ID
     * @return String token
     */
    public String getToken(String appId) throws UnsupportedEncodingException {
        String appSecret = getRequiredProperty(appId);
        String tenantId = getRequiredProperty("SNG_TENANT_ID");

        Map<String, String> params = new LinkedHashMap<>();
        params.put("appid", appId);
        params.put("secret", appSecret);
        params.put("tenantId", tenantId);

        return callSNGApi("SNG_GET_TOKEN_URL", params, "access_token");
    }

    /**
     * 根据授权码获取用户编号
     *
     * @param accessToken 访问令牌
     * @param code        授权码
     * @return String 用户编号
     */
    public String getUserNumber(String accessToken, String code) throws UnsupportedEncodingException {
        String tenantId = getRequiredProperty("SNG_TENANT_ID");

        Map<String, String> params = new LinkedHashMap<>();
        params.put("access_token", accessToken);
        params.put("code", code);
        params.put("tenantId", tenantId);

        return callSNGApi("SNG_GET_USER_INFO_URL", params, "account");
    }

    /**
     * 通用 SNG API 调用
     *
     * @param urlKey      配置文件中的 URL key
     * @param params      查询参数
     * @param resultField 成功响应中要取值的 JSON 字段名
     */
    private String callSNGApi(String urlKey, Map<String, String> params, String resultField) throws UnsupportedEncodingException {
        String baseUrl = getRequiredProperty(urlKey);

        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(baseUrl);
        for (Map.Entry<String, String> entry : params.entrySet()) {
            builder.queryParam(entry.getKey(), entry.getValue());
        }
        String targetUrl = builder.toUriString();

        logger.info("请求URL：{}", targetUrl);

        Request request = new Request.Builder()
                .url(targetUrl)
                .get()
                .addHeader("Accept", "*/*")
                .build();

        try (Response response = HTTP_CLIENT.newCall(request).execute()) {
            if (!response.isSuccessful() || response.body() == null) {
                logger.error("请求失败，httpCode：{}", response.code());
                throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "请求失败：" + response.code());
            }
            String responseBody = response.body().string();
            logger.info("响应：{}", responseBody);

            JSONObject json = JSONObject.parseObject(responseBody);
            String errcode = json.getString("errcode");
            if (!"0".equals(errcode)) {
                logger.error("业务失败，errcode: {} ,errmsg: {}", errcode, json.getString("errmsg"));
                throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR,
                        "业务失败: " + json.getString("errmsg"));
            }
            return json.getString(resultField);
        } catch (IOException e) {
            logger.error("请求异常：{}", e.getMessage());
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "请求异常：" + e.getMessage());
        }
    }

    /**
     * 获取配置项，若为空则抛出异常
     *
     * @param key 配置项key
     * @return 配置项值
     */
    private String getRequiredProperty(String key) {
        String value = getProperties().getProperty(key);
        if (StringUtils.isBlank(value)) {
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "配置项为空：" + key);
        }
        return value;
    }


    private Properties getProperties() {
        return PropertiesHolder.getProperties();
    }

    /**
     * 配置文件持有者，支持热更新。
     * 每30秒检查一次文件最后修改时间，仅在文件变更时重新加载，IO开销极小。
     */
    private static class PropertiesHolder {
        private static volatile Properties PROPERTIES;
        private static volatile long lastModified = -1;
        private static volatile long lastCheckTime;
        private static final long CHECK_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30);
        private static final ReentrantLock lock = new ReentrantLock();

        static Properties getProperties() {
            if (PROPERTIES != null && (System.currentTimeMillis() - lastCheckTime) <= CHECK_INTERVAL_MS) {
                return PROPERTIES;
            }
            return getPropertiesWithReload();
        }

        private static Properties getPropertiesWithReload() {
            if (PROPERTIES == null) {
                lock.lock();
            } else if (!lock.tryLock()) {
                // 如果锁被占用，则返回当前的PROPERTIES,避免多个线程同时加载配置文件 等待锁可用
                return PROPERTIES;
            }
            try {
                long now = System.currentTimeMillis();
                if (PROPERTIES != null && (now - lastCheckTime) <= CHECK_INTERVAL_MS) {
                    return PROPERTIES;
                }
                lastCheckTime = now;

                String configFile = System.getProperty("eas.properties.dir") + "/SNGConfig.properties";
                File file = new File(configFile);
                if (!file.exists()) {
                    if (PROPERTIES == null) {
                        throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, "配置文件不存在!");
                    }
                    logger.warn("配置文件已删除，继续使用缓存: {}", configFile);
                    return PROPERTIES;
                }

                long currentModified = file.lastModified();
                if (PROPERTIES == null || currentModified != lastModified) {
                    PROPERTIES = PropertiesUtil.getProperties(configFile);
                    lastModified = currentModified;
                    logger.info("配置文件已加载: {}", configFile);
                }
                return PROPERTIES;
            } finally {
                lock.unlock();
            }
        }
    }
}
