package com.easyagents.document.core.mineru;

import com.easyagents.core.util.StringUtil;
import com.easyagents.document.core.DocumentParseService;
import com.easyagents.document.core.exception.DocumentParseException;
import com.easyagents.document.core.entity.ParseFile;
import com.easyagents.document.core.entity.ParseRequest;
import com.easyagents.document.core.entity.ParseResponse;
import com.easyagents.document.core.entity.ParseTaskInfo;
import com.easyagents.document.core.entity.ParseTaskStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;

/**
 * 基于 MinerU API 的文档解析服务，支持 docx 文档和 pdf 文档。
 *
 * @author Codex
 * @since 2026-04-16
 */
public class MineruDocumentParseService<R extends ParseRequest> implements DocumentParseService<R> {

    public static final String PROVIDER_NAME = "mineru";

    private static final Logger LOG = LoggerFactory.getLogger(MineruDocumentParseService.class);

    private final MineruProperties properties;
    private final MineruClient client;
    private final MineruMapper mapper;

    /**
     * 创建默认服务实例。
     *
     * @param properties MinerU 配置
     */
    public MineruDocumentParseService(MineruProperties properties) {
        this(properties, new MineruMapper(properties));
    }

    /**
     * 创建默认服务实例。
     *
     * @param properties MinerU 配置
     * @param mapper 结果映射器
     */
    public MineruDocumentParseService(MineruProperties properties, MineruMapper mapper) {
        this(properties, new MineruClient(properties, mapper), mapper);
    }

    /**
     * 创建服务实例。
     *
     * @param properties MinerU 配置
     * @param client HTTP 客户端
     * @param mapper 结果映射器
     */
    public MineruDocumentParseService(MineruProperties properties, MineruClient client, MineruMapper mapper) {
        this.properties = properties;
        this.client = client;
        this.mapper = mapper;
    }

    @Override
    public ParseResponse parse(ParseRequest request) {
        ParseRequest normalizedRequest = normalizeRequest(request);
        LOG.info("MinerU 开始同步解析: provider={}, fileCount={}, backend={}",
            PROVIDER_NAME,
            normalizedRequest.getFiles() == null ? 0 : normalizedRequest.getFiles().size(),
            normalizedRequest.getBackend());
        ParseResponse response = mapper.toParseResponse(client.parse(normalizedRequest));
        LOG.info("MinerU 同步解析完成: provider={}, fileCount={}, resultCount={}",
            PROVIDER_NAME,
            normalizedRequest.getFiles() == null ? 0 : normalizedRequest.getFiles().size(),
            response == null || response.getResults() == null ? 0 : response.getResults().size());
        return response;
    }

    @Override
    public ParseTaskStatus submit(ParseRequest request) {
        ParseRequest normalizedRequest = normalizeRequest(request);
        normalizedRequest.setReturnMarkdown(true);
        normalizedRequest.setReturnMiddleJson(true);
        normalizedRequest.setReturnContentList(true);
        normalizedRequest.setReturnModelOutput(true);
        normalizedRequest.setReturnImages(true);
        LOG.info("MinerU 开始提交异步解析任务: provider={}, fileCount={}, backend={}",
            PROVIDER_NAME,
            normalizedRequest.getFiles() == null ? 0 : normalizedRequest.getFiles().size(),
            normalizedRequest.getBackend());
        ParseTaskStatus taskStatus = mapper.toParseTaskStatus(client.submit(normalizedRequest));
        LOG.info("MinerU 异步解析任务提交完成: provider={}, taskId={}, status={}",
            PROVIDER_NAME,
            taskStatus == null ? null : taskStatus.getTaskId(),
            taskStatus == null ? null : taskStatus.getStatus());
        return taskStatus;
    }

    @Override
    public ParseTaskStatus queryTask(String taskId) {
        validateTaskId(taskId);
        ParseTaskStatus taskStatus = mapper.toParseTaskStatus(client.queryTask(taskId));
        LOG.info("MinerU 查询异步任务状态: provider={}, taskId={}, status={}",
            PROVIDER_NAME,
            taskId,
            taskStatus == null ? null : taskStatus.getStatus());
        return taskStatus;
    }

    @Override
    public ParseResponse queryResult(String taskId) {
        validateTaskId(taskId);
        LOG.info("MinerU 开始获取异步解析结果: provider={}, taskId={}", PROVIDER_NAME, taskId);
        MineruTaskStatus taskStatus = waitForTaskCompleted(taskId);
        ParseResponse response = mapper.fromZip(client.queryResultZip(taskId));
        mapper.enrichAsyncResponse(response, taskStatus.getBackend(), taskStatus.getVersion());
        LOG.info("MinerU 获取异步解析结果完成: provider={}, taskId={}, resultCount={}",
            PROVIDER_NAME,
            taskId,
            response == null || response.getResults() == null ? 0 : response.getResults().size());
        return response;
    }

    @Override
    public ParseTaskInfo queryTaskInfo(String taskId) {
        validateTaskId(taskId);
        MineruTaskStatus taskStatus = client.queryTask(taskId);
        ParseTaskInfo taskInfo = ParseTaskInfo.fromStatus(mapper.toParseTaskStatus(taskStatus));
        if ("completed".equalsIgnoreCase(taskStatus.getStatus())) {
            ParseResponse response = mapper.fromZip(client.queryResultZip(taskId));
            mapper.enrichAsyncResponse(response, taskStatus.getBackend(), taskStatus.getVersion());
            taskInfo.setResult(response);
        }
        LOG.info("MinerU 查询任务聚合信息: provider={}, taskId={}, status={}, hasResult={}",
            PROVIDER_NAME,
            taskId,
            taskInfo.getStatus(),
            taskInfo.getResult() != null);
        return taskInfo;
    }

    /**
     * 获取 MinerU 配置。
     *
     * @return MinerU 配置
     */
    protected MineruProperties getProperties() {
        return properties;
    }

    /**
     * 归一化解析请求，补齐默认参数。
     *
     * @param request 原始请求
     * @return 归一化后的请求
     */
    protected ParseRequest normalizeRequest(ParseRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("ParseRequest must not be null");
        }
        if (request.getFiles() == null || request.getFiles().isEmpty()) {
            throw new IllegalArgumentException("ParseRequest files must not be empty");
        }
        ParseRequest normalizedRequest = new ParseRequest();
        normalizedRequest.setFiles(new ArrayList<ParseFile>(request.getFiles()));
        normalizedRequest.setBackend(StringUtil.hasText(request.getBackend()) ? request.getBackend() : properties.getDefaultBackend());
        normalizedRequest.setLanguages(
            request.getLanguages() == null || request.getLanguages().isEmpty()
                ? new ArrayList<String>(properties.getDefaultLangList())
                : new ArrayList<String>(request.getLanguages())
        );
        normalizedRequest.setReturnMarkdown(request.getReturnMarkdown() == null ? Boolean.TRUE : request.getReturnMarkdown());
        normalizedRequest.setReturnMiddleJson(request.getReturnMiddleJson() == null ? Boolean.TRUE : request.getReturnMiddleJson());
        normalizedRequest.setReturnContentList(request.getReturnContentList() == null ? Boolean.TRUE : request.getReturnContentList());
        normalizedRequest.setReturnModelOutput(request.getReturnModelOutput() == null ? Boolean.FALSE : request.getReturnModelOutput());
        normalizedRequest.setReturnImages(request.getReturnImages() == null ? Boolean.TRUE : request.getReturnImages());
        return normalizedRequest;
    }

    /**
     * 校验任务 ID。
     *
     * @param taskId 任务 ID
     */
    protected void validateTaskId(String taskId) {
        if (!StringUtil.hasText(taskId)) {
            throw new IllegalArgumentException("taskId must not be empty");
        }
    }

    /**
     * 轮询任务状态直到完成或失败。
     *
     * @param taskId 任务 ID
     * @return 已完成的任务状态
     */
    protected MineruTaskStatus waitForTaskCompleted(String taskId) {
        long deadline = System.currentTimeMillis() + properties.getResultTimeoutMs();
        while (true) {
            MineruTaskStatus taskStatus = client.queryTask(taskId);
            if ("completed".equals(taskStatus.getStatus())) {
                return taskStatus;
            }
            if ("failed".equals(taskStatus.getStatus())) {
                throw new DocumentParseException("MinerU task failed: " + taskStatus.getError());
            }
            if (System.currentTimeMillis() >= deadline) {
                throw new DocumentParseException("MinerU task result timeout: " + taskId);
            }
            try {
                Thread.sleep(properties.getPollIntervalMs());
            } catch (InterruptedException exception) {
                Thread.currentThread().interrupt();
                throw new DocumentParseException("Interrupted while waiting for MinerU task: " + taskId, exception);
            }
        }
    }
}
