package com.writeoff.module.ocr.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.writeoff.common.exception.BusinessException;
import com.writeoff.common.exception.ErrorCodes;
import com.writeoff.module.ocr.config.BaiduOcrProperties;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

@Service
public class BaiduOcrTokenService {
    private final BaiduOcrProperties props;
    private final ObjectMapper objectMapper = new ObjectMapper();

    private final Object lock = new Object();
    private volatile String cachedToken;
    private volatile long cachedTokenExpireAtMs;

    public BaiduOcrTokenService(BaiduOcrProperties props) {
        this.props = props;
    }

    public String getAccessToken() {
        String token = cachedToken;
        long expireAt = cachedTokenExpireAtMs;
        long now = System.currentTimeMillis();
        if (token != null && now < expireAt) {
            return token;
        }
        synchronized (lock) {
            token = cachedToken;
            expireAt = cachedTokenExpireAtMs;
            now = System.currentTimeMillis();
            if (token != null && now < expireAt) {
                return token;
            }
            refreshTokenLocked();
            if (cachedToken == null) {
                throw new BusinessException(ErrorCodes.INTERNAL_ERROR, "获取百度OCR access_token失败");
            }
            return cachedToken;
        }
    }

    private void refreshTokenLocked() {
        String apiKey = String.valueOf(props.getApiKey() == null ? "" : props.getApiKey()).trim();
        String secretKey = String.valueOf(props.getSecretKey() == null ? "" : props.getSecretKey()).trim();
        if (apiKey.isEmpty() || secretKey.isEmpty()) {
            throw new BusinessException(ErrorCodes.INTERNAL_ERROR, "百度OCR未配置 BAIDU_OCR_API_KEY/BAIDU_OCR_SECRET_KEY");
        }
        String tokenUrl = Objects.requireNonNull(props.getTokenUrl(), "tokenUrl");
        try {
            String query = "grant_type=client_credentials"
                + "&client_id=" + URLEncoder.encode(apiKey, "UTF-8")
                + "&client_secret=" + URLEncoder.encode(secretKey, "UTF-8");
            URL url = new URL(tokenUrl + "?" + query);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(Math.max(1000, props.getConnectTimeoutMs()));
            conn.setReadTimeout(Math.max(1000, props.getReadTimeoutMs()));

            int status = conn.getResponseCode();
            InputStream is = status >= 200 && status < 300 ? conn.getInputStream() : conn.getErrorStream();
            String body = readAll(is);
            JsonNode json = objectMapper.readTree(body == null ? "" : body);
            if (status < 200 || status >= 300) {
                String err = json.has("error_description") ? json.get("error_description").asText() : (body == null ? "" : body);
                throw new BusinessException(ErrorCodes.INTERNAL_ERROR, "百度OCR token获取失败: " + err);
            }
            String accessToken = json.has("access_token") ? json.get("access_token").asText() : null;
            long expiresInSec = json.has("expires_in") ? json.get("expires_in").asLong(0) : 0;
            if (accessToken == null || accessToken.trim().isEmpty() || expiresInSec <= 0) {
                throw new BusinessException(ErrorCodes.INTERNAL_ERROR, "百度OCR token返回缺少字段");
            }
            long now = System.currentTimeMillis();
            long safetyMs = 60_000L;
            long expireAtMs = now + Math.max(0, expiresInSec * 1000L - safetyMs);
            cachedToken = accessToken.trim();
            cachedTokenExpireAtMs = expireAtMs;
        } catch (BusinessException be) {
            throw be;
        } catch (Exception e) {
            throw new BusinessException(ErrorCodes.INTERNAL_ERROR, "百度OCR token获取异常: " + e.getMessage());
        }
    }

    private static String readAll(InputStream is) throws IOException {
        if (is == null) {
            return "";
        }
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        }
    }
}

