package tech.easyflow.ai.document.service.impl;

import com.easyagents.document.core.DocumentParseService;
import com.easyagents.document.core.entity.ParseResponse;
import com.easyagents.document.core.entity.ParseResult;
import com.easyagents.document.core.entity.ParseTaskInfo;
import com.easyagents.document.core.entity.ParseTaskStatus;
import com.easyagents.document.pdf.PdfDocumentParseService;
import com.easyagents.document.pptx.PptxDocumentParseService;
import com.easyagents.document.xlsx.XlsxDocumentParseService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tech.easyflow.ai.document.exception.DocumentParseBridgeException;
import tech.easyflow.ai.document.model.DocumentParseScenario;
import tech.easyflow.ai.document.model.DocumentSourceRef;
import tech.easyflow.ai.document.model.DocumentParseTaskInfo;
import tech.easyflow.ai.document.model.DocumentParseTaskStatus;
import tech.easyflow.ai.document.model.DocumentParsedResult;
import tech.easyflow.ai.document.service.DocumentParseBridgeService;
import tech.easyflow.ai.document.support.DocumentSourceLoader;
import tech.easyflow.ai.document.support.DocumentParseRequestFactory;
import tech.easyflow.ai.document.support.DocumentParseResultMapper;
import tech.easyflow.ai.document.support.DocumentParseSourceType;
import tech.easyflow.ai.document.support.LoadedDocumentSource;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.function.Function;

/**
 * 统一文档解析桥接门面默认实现。
 *
 * @author Codex
 * @since 2026-04-14
 */
@Service
public class DocumentParseBridgeServiceImpl implements DocumentParseBridgeService {

    private static final Logger LOG = LoggerFactory.getLogger(DocumentParseBridgeServiceImpl.class);
    private static final String DEFAULT_DOCUMENT_PARSE_SERVICE_BEAN_NAME = "documentParseService";

    @Nullable
    private final DocumentParseService defaultDocumentParseService;
    @Nullable
    private final PdfDocumentParseService pdfDocumentParseService;
    @Nullable
    private final PptxDocumentParseService pptxDocumentParseService;
    @Nullable
    private final XlsxDocumentParseService xlsxDocumentParseService;
    private final DocumentSourceLoader documentSourceLoader;
    private final DocumentParseRequestFactory parseRequestFactory;
    private final DocumentParseResultMapper parseResultMapper;

    public DocumentParseBridgeServiceImpl(@Nullable
                                          @Qualifier(DEFAULT_DOCUMENT_PARSE_SERVICE_BEAN_NAME)
                                          DocumentParseService defaultDocumentParseService,
                                          @Nullable PdfDocumentParseService pdfDocumentParseService,
                                          @Nullable PptxDocumentParseService pptxDocumentParseService,
                                          @Nullable XlsxDocumentParseService xlsxDocumentParseService,
                                          DocumentSourceLoader documentSourceLoader,
                                          DocumentParseRequestFactory parseRequestFactory,
                                          DocumentParseResultMapper parseResultMapper) {
        this.defaultDocumentParseService = defaultDocumentParseService;
        this.pdfDocumentParseService = pdfDocumentParseService;
        this.pptxDocumentParseService = pptxDocumentParseService;
        this.xlsxDocumentParseService = xlsxDocumentParseService;
        this.documentSourceLoader = documentSourceLoader;
        this.parseRequestFactory = parseRequestFactory;
        this.parseResultMapper = parseResultMapper;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DocumentParsedResult parse(DocumentSourceRef source, DocumentParseScenario scenario) {
        try {
            LoadedDocumentSource loadedSource = prepareSupportedSource(source);
            LOG.info("桥接服务开始同步解析文档: fileName={}, contentType={}, scenario={}",
                loadedSource.getFileName(), loadedSource.getContentType(), scenario);
            DocumentParseService parseService = resolveService(loadedSource);
            ParseResponse response = parseService.parse(parseRequestFactory.build(loadedSource, scenario));
            DocumentParsedResult result = parseResultMapper.map(extractSingleResult(response, false));
            LOG.info("桥接服务同步解析完成: fileName={}, scenario={}, preferredTextLength={}",
                loadedSource.getFileName(), scenario, resolveTextLength(result));
            return result;
        } catch (DocumentParseBridgeException e) {
            LOG.error("桥接服务同步解析失败: fileName={}, scenario={}",
                source == null ? null : source.getFileName(), scenario, e);
            throw e;
        } catch (Exception e) {
            LOG.error("桥接服务同步解析异常: fileName={}, scenario={}",
                source == null ? null : source.getFileName(), scenario, e);
            throw DocumentParseBridgeException.parseFailed("同步文档解析失败", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DocumentParseTaskStatus submit(DocumentSourceRef source, DocumentParseScenario scenario) {
        try {
            LoadedDocumentSource loadedSource = prepareSupportedSource(source);
            LOG.info("桥接服务开始提交异步解析任务: fileName={}, contentType={}, scenario={}",
                loadedSource.getFileName(), loadedSource.getContentType(), scenario);
            DocumentParseService parseService = resolveService(loadedSource);
            ParseTaskStatus taskStatus = parseService.submit(parseRequestFactory.build(loadedSource, scenario));
            DocumentParseTaskStatus mappedStatus = parseResultMapper.map(taskStatus);
            LOG.info("桥接服务异步解析任务提交完成: fileName={}, scenario={}, providerTaskId={}, status={}",
                loadedSource.getFileName(), scenario, mappedStatus.getTaskId(), mappedStatus.getStatus());
            return mappedStatus;
        } catch (DocumentParseBridgeException e) {
            LOG.error("桥接服务提交异步解析任务失败: fileName={}, scenario={}",
                source == null ? null : source.getFileName(), scenario, e);
            throw e;
        } catch (Exception e) {
            LOG.error("桥接服务提交异步解析任务异常: fileName={}, scenario={}",
                source == null ? null : source.getFileName(), scenario, e);
            throw DocumentParseBridgeException.taskFailed("提交异步文档解析任务失败", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DocumentParseTaskStatus queryTask(String taskId) {
        if (!StringUtils.hasText(taskId)) {
            throw DocumentParseBridgeException.taskFailed("taskId 不能为空");
        }
        try {
            ParseTaskStatus taskStatus = executeAgainstTaskService(taskId, service -> service.queryTask(taskId));
            return parseResultMapper.map(taskStatus);
        } catch (DocumentParseBridgeException e) {
            throw e;
        } catch (Exception e) {
            throw DocumentParseBridgeException.taskFailed("查询异步文档解析任务状态失败", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DocumentParsedResult queryResult(String taskId) {
        if (!StringUtils.hasText(taskId)) {
            throw DocumentParseBridgeException.resultFetchFailed("taskId 不能为空");
        }
        try {
            LOG.info("桥接服务开始获取异步解析结果: providerTaskId={}", taskId);
            ParseResponse response = executeAgainstTaskService(taskId, service -> service.queryResult(taskId));
            DocumentParsedResult result = parseResultMapper.map(extractSingleResult(response, true));
            LOG.info("桥接服务获取异步解析结果完成: providerTaskId={}, preferredTextLength={}",
                taskId, resolveTextLength(result));
            return result;
        } catch (DocumentParseBridgeException e) {
            LOG.error("桥接服务获取异步解析结果失败: providerTaskId={}", taskId, e);
            throw e;
        } catch (Exception e) {
            LOG.error("桥接服务获取异步解析结果异常: providerTaskId={}", taskId, e);
            throw DocumentParseBridgeException.resultFetchFailed("获取异步文档解析结果失败", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public DocumentParseTaskInfo queryTaskInfo(String taskId) {
        if (!StringUtils.hasText(taskId)) {
            throw DocumentParseBridgeException.taskFailed("taskId 不能为空");
        }
        try {
            ParseTaskInfo taskInfo = executeAgainstTaskService(taskId, service -> service.queryTaskInfo(taskId));
            DocumentParseTaskInfo mappedTaskInfo = parseResultMapper.map(taskInfo);
            LOG.info("桥接服务查询异步解析任务状态: providerTaskId={}, status={}, hasResult={}",
                taskId,
                mappedTaskInfo == null ? null : mappedTaskInfo.getStatus(),
                mappedTaskInfo != null && mappedTaskInfo.getResult() != null);
            return mappedTaskInfo;
        } catch (DocumentParseBridgeException e) {
            LOG.error("桥接服务查询异步解析任务状态失败: providerTaskId={}", taskId, e);
            throw e;
        } catch (Exception e) {
            LOG.error("桥接服务查询异步解析任务状态异常: providerTaskId={}", taskId, e);
            throw DocumentParseBridgeException.taskFailed("聚合查询异步文档解析任务信息失败", e);
        }
    }

    private int resolveTextLength(DocumentParsedResult result) {
        String text = result == null ? null : result.getPreferredText();
        if (!StringUtils.hasText(text) && result != null) {
            text = result.getMarkdown();
        }
        if (!StringUtils.hasText(text) && result != null) {
            text = result.getPlainText();
        }
        return text == null ? 0 : text.length();
    }

    private LoadedDocumentSource prepareSupportedSource(DocumentSourceRef source) {
        LoadedDocumentSource loadedSource = documentSourceLoader.load(source);
        if (!isSupportedByBridge(loadedSource)) {
            throw DocumentParseBridgeException.unsupportedSource("统一文档解析桥接当前仅支持 PDF、DOCX、PPTX、XLSX 文件");
        }
        return loadedSource;
    }

    private boolean isSupportedByBridge(LoadedDocumentSource loadedSource) {
        return DocumentParseSourceType.resolve(loadedSource.getFileName(), loadedSource.getContentType()) != DocumentParseSourceType.UNSUPPORTED;
    }

    private DocumentParseService resolveService(LoadedDocumentSource loadedSource) {
        DocumentParseSourceType sourceType = DocumentParseSourceType.resolve(loadedSource.getFileName(), loadedSource.getContentType());
        switch (sourceType) {
            case PDF:
                return requireSpecificService(pdfDocumentParseService, defaultDocumentParseService, "PDF");
            case DOCX:
                return requireSpecificService(defaultDocumentParseService, null, "DOCX");
            case PPTX:
                return requireSpecificService(pptxDocumentParseService, null, "PPTX");
            case XLSX:
                return requireSpecificService(xlsxDocumentParseService, null, "XLSX");
            default:
                throw DocumentParseBridgeException.unsupportedSource("当前文件类型暂不支持桥接解析");
        }
    }

    private DocumentParseService requireSpecificService(@Nullable DocumentParseService primaryService,
                                                        @Nullable DocumentParseService fallbackService,
                                                        String sourceType) {
        if (primaryService != null) {
            return primaryService;
        }
        if (fallbackService != null) {
            return fallbackService;
        }
        throw DocumentParseBridgeException.serviceNotEnabled("未启用 " + sourceType + " 文档解析服务");
    }

    private <T> T executeAgainstTaskService(String taskId, Function<DocumentParseService, T> action) {
        List<DocumentParseService> services = availableServices();
        if (services.isEmpty()) {
            throw DocumentParseBridgeException.serviceNotEnabled();
        }
        Exception lastException = null;
        for (DocumentParseService service : services) {
            try {
                return action.apply(service);
            } catch (Exception exception) {
                lastException = exception;
                LOG.debug("桥接服务任务查询尝试失败，准备切换下一个解析服务: taskId={}, service={}",
                    taskId,
                    service.getClass().getSimpleName(),
                    exception);
            }
        }
        if (lastException instanceof RuntimeException) {
            throw (RuntimeException) lastException;
        }
        throw DocumentParseBridgeException.taskFailed("未找到可处理当前任务ID的文档解析服务", lastException);
    }

    private List<DocumentParseService> availableServices() {
        LinkedHashSet<DocumentParseService> services = new LinkedHashSet<DocumentParseService>();
        if (pptxDocumentParseService != null) {
            services.add(pptxDocumentParseService);
        }
        if (xlsxDocumentParseService != null) {
            services.add(xlsxDocumentParseService);
        }
        if (pdfDocumentParseService != null) {
            services.add(pdfDocumentParseService);
        }
        if (defaultDocumentParseService != null) {
            services.add(defaultDocumentParseService);
        }
        return new ArrayList<DocumentParseService>(services);
    }

    private ParseResult extractSingleResult(ParseResponse response, boolean resultFetchPhase) {
        if (response == null || response.getResults() == null || response.getResults().isEmpty()) {
            if (resultFetchPhase) {
                throw DocumentParseBridgeException.resultFetchFailed("异步文档解析结果为空");
            }
            throw DocumentParseBridgeException.parseFailed("同步文档解析结果为空");
        }
        return response.getResults().get(0);
    }
}
