package com.lingyue.ai.client;

import com.lingyue.ai.config.NerClientConfig;
import com.lingyue.ai.dto.ner.*;
import com.lingyue.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.util.retry.Retry;

import java.time.Duration;
import java.util.List;

/**
 * Python NER 服务 HTTP 客户端
 *
 * @author lingyue
 * @since 2026-01-19
 */
@Slf4j
@Component
public class PythonNerClient {

    private final WebClient webClient;
    private final NerClientConfig config;

    public PythonNerClient(NerClientConfig config) {
        this.config = config;
        this.webClient = WebClient.builder()
                .baseUrl(config.getUrl())
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
                .build();
        log.info("PythonNerClient 初始化完成: url={}, timeout={}ms", config.getUrl(), config.getTimeout());
    }

    /**
     * 提取实体
     *
     * @param request NER 请求
     * @return NER 响应
     */
    public NerResponse extractEntities(NerRequest request) {
        log.debug("调用 Python NER 服务提取实体: documentId={}", request.getDocumentId());
        
        try {
            NerResponse response = webClient
                    .post()
                    .uri("/ner/extract")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(NerResponse.class)
                    .timeout(Duration.ofMillis(config.getTimeout()))
                    .retryWhen(Retry.backoff(config.getMaxRetries(), Duration.ofMillis(config.getRetryInterval()))
                            .filter(this::isRetryable)
                            .doBeforeRetry(signal -> log.warn("NER 服务调用失败，正在重试: attempt={}", 
                                    signal.totalRetries() + 1)))
                    .block();

            if (response == null) {
                throw new ServiceException("Python NER 服务返回空响应");
            }

            if (!response.getSuccess()) {
                throw new ServiceException("NER 提取失败: " + response.getErrorMessage());
            }

            log.info("NER 提取成功: documentId={}, entityCount={}, relationCount={}", 
                    request.getDocumentId(), response.getEntityCount(), response.getRelationCount());

            return response;

        } catch (WebClientResponseException e) {
            log.error("Python NER 服务调用失败: status={}, body={}", e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException("NER 服务调用失败: " + e.getMessage(), e);
        } catch (Exception e) {
            if (e instanceof ServiceException) {
                throw e;
            }
            log.error("Python NER 服务调用异常: {}", e.getMessage(), e);
            throw new ServiceException("NER 服务调用异常: " + e.getMessage(), e);
        }
    }

    /**
     * 提取关系
     *
     * @param request 关系抽取请求
     * @return 关系抽取响应
     */
    public RelationResponse extractRelations(RelationRequest request) {
        log.debug("调用 Python NER 服务提取关系: documentId={}, entityCount={}", 
                request.getDocumentId(), 
                request.getEntities() != null ? request.getEntities().size() : 0);
        
        try {
            RelationResponse response = webClient
                    .post()
                    .uri("/ner/relations")
                    .bodyValue(request)
                    .retrieve()
                    .bodyToMono(RelationResponse.class)
                    .timeout(Duration.ofMillis(config.getTimeout()))
                    .retryWhen(Retry.backoff(config.getMaxRetries(), Duration.ofMillis(config.getRetryInterval()))
                            .filter(this::isRetryable)
                            .doBeforeRetry(signal -> log.warn("关系抽取服务调用失败，正在重试: attempt={}", 
                                    signal.totalRetries() + 1)))
                    .block();

            if (response == null) {
                throw new ServiceException("Python NER 服务返回空响应");
            }

            if (!response.getSuccess()) {
                throw new ServiceException("关系抽取失败: " + response.getErrorMessage());
            }

            log.info("关系抽取成功: documentId={}, relationCount={}", 
                    request.getDocumentId(), response.getRelationCount());

            return response;

        } catch (WebClientResponseException e) {
            log.error("Python NER 关系抽取服务调用失败: status={}, body={}", 
                    e.getStatusCode(), e.getResponseBodyAsString());
            throw new ServiceException("关系抽取服务调用失败: " + e.getMessage(), e);
        } catch (Exception e) {
            if (e instanceof ServiceException) {
                throw e;
            }
            log.error("Python NER 关系抽取服务调用异常: {}", e.getMessage(), e);
            throw new ServiceException("关系抽取服务调用异常: " + e.getMessage(), e);
        }
    }

    /**
     * 健康检查
     *
     * @return 是否健康
     */
    public boolean healthCheck() {
        try {
            String response = webClient
                    .get()
                    .uri("/health")
                    .retrieve()
                    .bodyToMono(String.class)
                    .timeout(Duration.ofMillis(config.getConnectTimeout()))
                    .block();
            
            return response != null && response.contains("ok");
        } catch (Exception e) {
            log.warn("Python NER 服务健康检查失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 判断异常是否可重试
     */
    private boolean isRetryable(Throwable throwable) {
        if (throwable instanceof WebClientResponseException) {
            WebClientResponseException e = (WebClientResponseException) throwable;
            // 5xx 错误可重试
            return e.getStatusCode().is5xxServerError();
        }
        // 连接超时、读取超时可重试
        return throwable instanceof java.net.ConnectException ||
               throwable instanceof java.net.SocketTimeoutException ||
               throwable instanceof java.util.concurrent.TimeoutException;
    }

    /**
     * 快捷方法：仅提取实体（不提取关系）
     */
    public List<EntityInfo> extractEntitiesOnly(String documentId, String text) {
        NerRequest request = NerRequest.builder()
                .documentId(documentId)
                .text(text)
                .extractRelations(false)
                .build();
        
        NerResponse response = extractEntities(request);
        return response.getEntities();
    }

    /**
     * 快捷方法：提取实体和关系
     */
    public NerResponse extractEntitiesAndRelations(String documentId, String text) {
        NerRequest request = NerRequest.builder()
                .documentId(documentId)
                .text(text)
                .extractRelations(true)
                .build();
        
        return extractEntities(request);
    }

    /**
     * 快捷方法：提取指定类型的实体
     */
    public List<EntityInfo> extractEntitiesByTypes(String documentId, String text, List<String> entityTypes) {
        NerRequest request = NerRequest.builder()
                .documentId(documentId)
                .text(text)
                .entityTypes(entityTypes)
                .extractRelations(false)
                .build();
        
        NerResponse response = extractEntities(request);
        return response.getEntities();
    }
}
