package cn.iocoder.yudao.module.pms.hik;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class HikHttpClient {
    
    private Map<String, CloseableHttpClient> clientCache = new ConcurrentHashMap<>();
    
    public CloseableHttpClient getHttpClient(String username, String password, int timeout) {
        String key = username + "@" + password;
        return clientCache.computeIfAbsent(key, k -> createHttpClient(username, password, timeout));
    }
    
    private CloseableHttpClient createHttpClient(String username, String password, int timeout) {
        CredentialsProvider provider = new BasicCredentialsProvider();
        provider.setCredentials(
            AuthScope.ANY,
            new UsernamePasswordCredentials(username, password)
        );
        
        RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(timeout)
            .setSocketTimeout(timeout)
            .setConnectionRequestTimeout(timeout)
            .build();
        
        return HttpClients.custom()
            .setDefaultCredentialsProvider(provider)
            .setDefaultRequestConfig(requestConfig)
            .build();
    }
    
    public String executeRequest(HttpRequestBase request, String username, 
                               String password, int timeout) throws IOException {
        CloseableHttpClient client = getHttpClient(username, password, timeout);
        try (CloseableHttpResponse response = client.execute(request)) {
            return handleResponse(response);
        }
    }
    public byte[] executeRequestByte(HttpRequestBase request, String username,
                                     String password, int timeout) throws IOException {
        CloseableHttpClient client = getHttpClient(username, password, timeout);
        try (CloseableHttpResponse response = client.execute(request)) {
            return handleResponseByte(response);
        }
    }
    public String doGet(String url, String username, String password, 
                       int timeout, Map<String, String> headers) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        setHeaders(httpGet, headers);
        return executeRequest(httpGet, username, password, timeout);
    }
    public byte[] doGetByte(String url, String username, String password,
                        int timeout, Map<String, String> headers) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        setHeaders(httpGet, headers);
        return executeRequestByte(httpGet, username, password, timeout);
    }
    
    public String doPost(String url, String body, String username, String password,
                        int timeout, Map<String, String> headers) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        setHeaders(httpPost, headers);
        if (body != null) {
            httpPost.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
        }
        return executeRequest(httpPost, username, password, timeout);
    }
    
    public String doPut(String url, String body, String username, String password,
                       int timeout, Map<String, String> headers) throws IOException {
        HttpPut httpPut = new HttpPut(url);
        setHeaders(httpPut, headers);
        if (body != null) {
            httpPut.setEntity(new StringEntity(body, StandardCharsets.UTF_8));
        }
        return executeRequest(httpPut, username, password, timeout);
    }
    
    private void setHeaders(HttpRequestBase request, Map<String, String> headers) {
        if (headers != null) {
            headers.forEach(request::setHeader);
        }
        // 默认JSON头
        if (request.getFirstHeader("Content-Type") == null) {
            request.setHeader("Content-Type", "application/json");
        }
    }
    
    private String handleResponse(HttpResponse response) throws IOException {
        int statusCode = response.getStatusLine().getStatusCode();
        HttpEntity entity = response.getEntity();
        String responseString = EntityUtils.toString(entity, StandardCharsets.UTF_8);
        
        log.debug("HTTP Status: {}, Response: {}", statusCode, responseString);
        
        if (statusCode >= 200 && statusCode < 300) {
            return responseString;
        } else {
            log.error("HTTP Error: {} - {}", statusCode, responseString);
            throw new IOException("HTTP Error: " + statusCode);
        }
    }

    private byte[] handleResponseByte(HttpResponse response) throws IOException {
        // 1. 获取响应状态码和响应体
        int statusCode = response.getStatusLine().getStatusCode();
        HttpEntity entity = response.getEntity();

        // 2. 直接读取二进制数据（核心改动：不再转String）
        byte[] responseBytes = entity != null ? EntityUtils.toByteArray(entity) : new byte[0];

        // 3. 日志打印优化：二进制数据不直接打印（避免乱码/日志过大），仅打印长度
        log.debug("HTTP Status: {}, Response Byte Length: {}", statusCode, responseBytes.length);

        // 4. 状态码校验
        if (statusCode >= 200 && statusCode < 300) {
            return responseBytes; // 直接返回byte[]
        } else {
            // 异常时：若有文本响应（如错误信息），可转String打印；无则打印长度
            String errorMsg = entity != null ?
                    EntityUtils.toString(entity, StandardCharsets.UTF_8) :
                    "No response body (byte length: " + responseBytes.length + ")";
            log.error("HTTP Error: {} - {}", statusCode, errorMsg);
            throw new IOException("HTTP Error: " + statusCode + ", Message: " + errorMsg);
        }
    }

    @PreDestroy
    public void destroy() {
        clientCache.values().forEach(client -> {
            try {
                client.close();
            } catch (IOException e) {
                log.error("Error closing HTTP client", e);
            }
        });
    }
}