package com.kingdee.eas.custom.beisen.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import okhttp3.*;
import org.apache.log4j.Logger;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BeisenTokenManager {
    private static final Logger logger = Logger.getLogger(BeisenTokenManager.class);
    private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
    private static final long EXPIRY_BUFFER = 300_000; // 5分钟缓冲时间

    private final OkHttpClient httpClient;
    private final Lock lock = new ReentrantLock();
    private volatile TokenState tokenState;

    private final String appKey;
    private final String appSecret;
    private final String tokenUrl;

    // 添加单例实例
    private static volatile BeisenTokenManager instance;

    // 私有化构造器
    private BeisenTokenManager() {
        this(Config.APP_KEY, Config.APP_SECRET, Config.TOKEN_URL);
    }


    // 获取单例方法
    public static BeisenTokenManager getInstance() {
        if (instance == null) {
            synchronized (BeisenTokenManager.class) {
                if (instance == null) {
                    instance = new BeisenTokenManager();
                }
            }
        }
        return instance;
    }

    public BeisenTokenManager(String appKey, String appSecret, String tokenUrl) {
        this.appKey = appKey;
        this.appSecret = appSecret;
        this.tokenUrl = tokenUrl;
        this.httpClient = buildHttpClient();
        this.tokenState = loadInitialToken();
    }

    public String getAccessToken() throws IOException {
        TokenState currentState = tokenState;
        if (currentState != null && !isTokenExpired(currentState)) {
            return currentState.getAccessToken();
        }

        if (currentState == null || isTokenExpired(currentState)) {
            lock.lock();
            try {
                currentState = tokenState;
                if (currentState == null || isTokenExpired(currentState)) {
                    currentState = fetchNewToken();
                    tokenState = currentState;
                }
            } finally {
                lock.unlock();
            }
        }
        return currentState.getAccessToken();
    }

    public void refreshToken() throws IOException {
        TokenState currentState = tokenState;
        if (currentState == null || currentState.getRefreshToken() == null) {
            tokenState = fetchNewToken();
            return;
        }

        if (isTokenExpired(currentState)) {
            lock.lock();
            try {
                currentState = tokenState;
                if (isTokenExpired(currentState)) {
                    tokenState = refreshToken(currentState.getRefreshToken());
                }
            } finally {
                lock.unlock();
            }
        }
    }

    private boolean isTokenExpired(TokenState state) {
        // 添加缓冲时间，避免在token即将过期时使用
        return System.currentTimeMillis() > (state.getExpireTime() - EXPIRY_BUFFER);
    }

    private TokenState loadInitialToken() {
        try {
            return fetchNewToken();
        } catch (Exception e) {
            logger.error("Failed to initialize token", e);
            throw new RuntimeException("Token initialization failed", e);
        }
    }

    private TokenState fetchNewToken() throws IOException {
        JSONObject requestBody = new JSONObject();
        requestBody.put("grant_type", "client_credentials");
        requestBody.put("app_key", appKey);
        requestBody.put("app_secret", appSecret);

        return executeTokenRequest(requestBody);
    }

    private TokenState refreshToken(String refreshToken) throws IOException {
        JSONObject requestBody = new JSONObject();
        requestBody.put("grant_type", "refresh_token");
        requestBody.put("refresh_token", refreshToken);

        return executeTokenRequest(requestBody);
    }

    private TokenState executeTokenRequest(JSONObject requestBody) throws IOException {
        Request request = new Request.Builder()
                .url(tokenUrl)
                .post(RequestBody.create(JSON_MEDIA_TYPE, requestBody.toJSONString()))
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                String errorBody = response.body() != null ? response.body().string() : "null";
                logger.error("Token request failed. Code: " + response.code() + ", Body: " + errorBody);
                throw new IOException("Token request failed with code: " + response.code());
            }

            String responseBody = response.body().string();
            JSONObject jsonResponse = JSON.parseObject(responseBody);

            String accessToken = jsonResponse.getString("access_token");
            String refreshToken = jsonResponse.getString("refresh_token");
            Long expiresIn = jsonResponse.getLong("expires_in");

            // 更健壮的空值检查
            if (accessToken == null || expiresIn == null || expiresIn <= 0) {
                throw new IOException("Invalid token response: " + responseBody);
            }

            return new TokenState(
                    accessToken,
                    System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiresIn),
                    refreshToken
            );
        }
    }

    private OkHttpClient buildHttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true)
                .build();
    }

    private static class TokenState {
        private final String accessToken;
        private final long expireTime;
        private final String refreshToken;

        public TokenState(String accessToken, long expireTime, String refreshToken) {
            this.accessToken = accessToken;
            this.expireTime = expireTime;
            this.refreshToken = refreshToken;
        }

        public String getAccessToken() {
            return accessToken;
        }

        public long getExpireTime() {
            return expireTime;
        }

        public String getRefreshToken() {
            return refreshToken;
        }
    }

    public static class Config {
        public static String APP_KEY = "6200E9EEE80C440B8342B1F8E8F0DFFE";
        public static String APP_SECRET = "BCDF24366FBA4851AEAE2638085548B1D780130E808842049FA7FDDD6D63B18D";
        public static String TOKEN_URL = "https://openapi.italent.cn/token";

        static {
            //从配置文件中读取配置
            try {
                BeisenParamByProperties beisenParamByProperties = new BeisenParamByProperties();
                Map<String, String> config = beisenParamByProperties.getConfig();
                //如果配置文件中存在配置，则覆盖默认配置
                if (config != null) {
                    //如果配置文件中存在appKey，则覆盖默认appKey
                    if (config.containsKey("appKey")) {
                        APP_KEY = config.get("appKey");
                    }
                    //如果配置文件中存在appSecret，则覆盖默认appSecret
                    if (config.containsKey("appSecret")) {
                        APP_SECRET = config.get("appSecret");
                    }
                    //如果配置文件中存在tokenUrl，则覆盖默认tokenUrl
                    if (config.containsKey("tokenUrl")) {
                        TOKEN_URL = config.get("tokenUrl");
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}