package com.lingyue.parse.service;

import com.lingyue.common.exception.ServiceException;
import com.lingyue.parse.config.PaddleOcrProperties;
import com.lingyue.parse.util.ErrorCategory;
import com.lingyue.parse.util.RetryUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * PaddleOCR 客户端，用于调用本地 GPU 服务器上的 OCR 接口。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class PaddleOcrClient {

    private final PaddleOcrProperties ocrProperties;

    /**
     * 对本地文件执行 OCR 识别，返回原始 JSON 字符串
     * 带重试机制和错误分类
     *
     * @param filePath 待识别的文件路径
     * @return OCR 结果 JSON 字符串
     */
    public String ocrFile(String filePath) {
        Path path = Path.of(filePath);
        if (!Files.exists(path)) {
            throw new ServiceException("待识别文件不存在: " + filePath);
        }

        // 使用重试机制调用OCR服务
        return RetryUtil.executeWithRetry(() -> {
            try {
                return doOcrFile(path, filePath);
            } catch (Exception e) {
                ErrorCategory category = ErrorCategory.categorize(e);
                log.error("OCR调用失败 [{}]: {}", category.getDescription(), e.getMessage());
                
                // 如果错误不可重试，直接抛出
                if (!category.isRetryable()) {
                    throw new ServiceException("OCR调用失败: " + e.getMessage(), e);
                }
                
                // 可重试的错误，抛出异常以便重试
                throw new RuntimeException("OCR调用失败: " + e.getMessage(), e);
            }
        }, "OCR识别", 3, 1000);
    }
    
    /**
     * 执行OCR调用的实际逻辑
     */
    private String doOcrFile(Path path, String filePath) throws IOException {
        byte[] bytes = Files.readAllBytes(path);

        // 构造 multipart/form-data 请求体，字段名为 file（可根据实际服务调整）
        ByteArrayResource fileResource = new ByteArrayResource(bytes) {
            @Override
            public String getFilename() {
                return path.getFileName().toString();
            }
        };

        MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
        body.add("file", fileResource);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);

        HttpEntity<MultiValueMap<String, Object>> requestEntity =
                new HttpEntity<>(body, headers);

        RestTemplate restTemplate = new RestTemplate();
        String url = ocrProperties.getServerUrl();

        log.info("调用 PaddleOCR 服务, url={}, file={}", url, filePath);

        try {
            ResponseEntity<String> response = restTemplate.exchange(
                    url,
                    HttpMethod.POST,
                    requestEntity,
                    String.class
            );

            if (!response.getStatusCode().is2xxSuccessful()) {
                throw new ServiceException("调用 OCR 服务失败, HTTP 状态码: " + response.getStatusCodeValue());
            }

            String bodyStr = response.getBody();
            log.debug("PaddleOCR 响应: {}", bodyStr);
            return bodyStr;
        } catch (RestClientException e) {
            // 网络相关异常，会被重试机制处理
            log.warn("OCR服务调用异常: {}", e.getMessage());
            throw e;
        }
    }
}


