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

import com.easyagents.document.core.DocumentParseService;
import com.easyagents.document.core.entity.ParseRequest;
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.junit.Assert;
import org.junit.Test;
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.support.DocumentSourceLoader;
import tech.easyflow.ai.document.support.DocumentParseRequestFactory;
import tech.easyflow.ai.document.support.DocumentParseResultMapper;
import tech.easyflow.common.filestorage.FileStorageService;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;

/**
 * {@link DocumentParseBridgeServiceImpl} 单元测试。
 *
 * @author Codex
 * @since 2026-04-14
 */
public class DocumentParseBridgeServiceImplTest {

    /**
     * 验证同步解析成功透传并返回标准化结果。
     */
    @Test
    public void shouldParseSuccessfully() {
        FakePdfDocumentParseService parseService = new FakePdfDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(parseService, null, null, parseService);

        DocumentParsedResult document = bridgeService.parse(buildSource(), DocumentParseScenario.WORKFLOW_TEXT);

        Assert.assertEquals("# demo", document.getPreferredText());
        Assert.assertFalse(parseService.lastParseRequest.getReturnMiddleJson());
        Assert.assertFalse(parseService.lastParseRequest.getReturnImages());
    }

    /**
     * 验证异步提交、状态查询和结果查询链路可用。
     */
    @Test
    public void shouldSupportAsyncFlow() {
        FakePdfDocumentParseService parseService = new FakePdfDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(parseService, null, null, parseService);

        DocumentParseTaskStatus taskStatus = bridgeService.submit(buildSource(), DocumentParseScenario.KNOWLEDGE_IMPORT);
        DocumentParseTaskStatus queriedStatus = bridgeService.queryTask("task-1");
        DocumentParsedResult queriedResult = bridgeService.queryResult("task-1");

        Assert.assertEquals("task-1", taskStatus.getTaskId());
        Assert.assertEquals("running", queriedStatus.getStatus());
        Assert.assertEquals("# demo", queriedResult.getPreferredText());
    }

    /**
     * 验证聚合查询在完成状态下会附带标准化结果。
     */
    @Test
    public void shouldQueryTaskInfoSuccessfully() {
        FakePdfDocumentParseService parseService = new FakePdfDocumentParseService();
        parseService.taskStatusValue = "completed";
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(parseService, null, null, parseService);

        DocumentParseTaskInfo taskInfo = bridgeService.queryTaskInfo("task-1");

        Assert.assertEquals("completed", taskInfo.getStatus());
        Assert.assertNotNull(taskInfo.getResult());
        Assert.assertEquals("# demo", taskInfo.getResult().getPreferredText());
    }

    /**
     * 验证缺少底层服务时抛出稳定错误码。
     */
    @Test
    public void shouldThrowWhenServiceDisabled() {
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(null, null, null, null);

        try {
            bridgeService.parse(buildSource(), DocumentParseScenario.WORKFLOW_TEXT);
            Assert.fail("expected DocumentParseBridgeException");
        } catch (DocumentParseBridgeException e) {
            Assert.assertEquals("service_not_enabled", e.getCode());
        }
    }

    @Test
    public void shouldRoutePptxToDedicatedService() {
        FakePptxDocumentParseService pptxService = new FakePptxDocumentParseService();
        FakeDefaultDocumentParseService defaultService = new FakeDefaultDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(null, pptxService, null, defaultService);

        DocumentParsedResult result = bridgeService.parse(buildSource("slides.pptx",
            "application/vnd.openxmlformats-officedocument.presentationml.presentation"), DocumentParseScenario.KNOWLEDGE_IMPORT);

        Assert.assertEquals("# pptx", result.getPreferredText());
        Assert.assertEquals(1, pptxService.parseCallCount);
        Assert.assertEquals(0, defaultService.parseCallCount);
    }

    /**
     * 验证 DOCX 会路由到默认桥接服务，而不是误走 PDF 服务。
     */
    @Test
    public void shouldRouteDocxToDefaultBridgeService() {
        FakePdfDocumentParseService pdfService = new FakePdfDocumentParseService();
        FakeDefaultDocumentParseService defaultService = new FakeDefaultDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(pdfService, null, null, defaultService);

        DocumentParsedResult result = bridgeService.parse(buildSource("demo.docx",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), DocumentParseScenario.WORKFLOW_TEXT);

        Assert.assertEquals("# docx", result.getPreferredText());
        Assert.assertEquals(1, defaultService.parseCallCount);
        Assert.assertEquals(0, pdfService.parseCallCount);
    }

    /**
     * 验证 DOCX 在缺少默认桥接服务时会明确失败，而不是退到 PDF 服务。
     */
    @Test
    public void shouldThrowWhenDocxBridgeServiceDisabled() {
        FakePdfDocumentParseService pdfService = new FakePdfDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(pdfService, null, null, null);

        try {
            bridgeService.parse(buildSource("demo.docx",
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document"), DocumentParseScenario.WORKFLOW_TEXT);
            Assert.fail("expected DocumentParseBridgeException");
        } catch (DocumentParseBridgeException e) {
            Assert.assertEquals("service_not_enabled", e.getCode());
            Assert.assertTrue(e.getMessage().contains("DOCX"));
        }
        Assert.assertEquals(0, pdfService.parseCallCount);
    }

    /**
     * 验证 XLSX 在缺少专用桥接服务时会明确失败。
     */
    @Test
    public void shouldThrowWhenXlsxBridgeServiceDisabled() {
        FakeDefaultDocumentParseService defaultService = new FakeDefaultDocumentParseService();
        DocumentParseBridgeServiceImpl bridgeService = buildBridgeService(null, null, null, defaultService);

        try {
            bridgeService.parse(buildSource("table.xlsx",
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"), DocumentParseScenario.WORKFLOW_TEXT);
            Assert.fail("expected DocumentParseBridgeException");
        } catch (DocumentParseBridgeException e) {
            Assert.assertEquals("service_not_enabled", e.getCode());
            Assert.assertTrue(e.getMessage().contains("XLSX"));
        }
        Assert.assertEquals(0, defaultService.parseCallCount);
    }

    private DocumentParseBridgeServiceImpl buildBridgeService(PdfDocumentParseService pdfDocumentParseService,
                                                              PptxDocumentParseService pptxDocumentParseService,
                                                              XlsxDocumentParseService xlsxDocumentParseService,
                                                              DocumentParseService parseService) {
        return new DocumentParseBridgeServiceImpl(
            parseService,
            pdfDocumentParseService,
            pptxDocumentParseService,
            xlsxDocumentParseService,
            new DocumentSourceLoader(new InMemoryFileStorageService()),
            new DocumentParseRequestFactory(),
            new DocumentParseResultMapper()
        );
    }

    private DocumentSourceRef buildSource() {
        return buildSource("demo.pdf", "application/pdf");
    }

    private DocumentSourceRef buildSource(String fileName, String contentType) {
        DocumentSourceRef sourceRef = DocumentSourceRef.ofBytes(fileName, "pdf-data".getBytes(StandardCharsets.UTF_8));
        sourceRef.setContentType(contentType);
        sourceRef.setSize(8L);
        return sourceRef;
    }

    private static class InMemoryFileStorageService implements FileStorageService {

        @Override
        public String save(org.springframework.web.multipart.MultipartFile file) {
            return null;
        }

        @Override
        public void delete(String path) {
        }

        @Override
        public InputStream readStream(String path) {
            return new ByteArrayInputStream("pdf-data".getBytes(StandardCharsets.UTF_8));
        }

        @Override
        public long getFileSize(String path) {
            return 8L;
        }
    }

    private static class FakePdfDocumentParseService implements PdfDocumentParseService {

        private ParseRequest lastParseRequest;
        private String taskStatusValue = "running";
        private int parseCallCount;

        @Override
        public ParseResponse parse(ParseRequest request) {
            parseCallCount++;
            this.lastParseRequest = request;
            return buildResponse();
        }

        @Override
        public ParseTaskStatus submit(ParseRequest request) {
            this.lastParseRequest = request;
            ParseTaskStatus status = new ParseTaskStatus();
            status.setTaskId("task-1");
            status.setStatus("submitted");
            status.setFileNames(Collections.singletonList("demo.pdf"));
            return status;
        }

        @Override
        public ParseTaskStatus queryTask(String taskId) {
            ParseTaskStatus status = new ParseTaskStatus();
            status.setTaskId(taskId);
            status.setStatus(taskStatusValue);
            status.setFileNames(Collections.singletonList("demo.pdf"));
            return status;
        }

        @Override
        public ParseResponse queryResult(String taskId) {
            return buildResponse();
        }

        @Override
        public ParseTaskInfo queryTaskInfo(String taskId) {
            ParseTaskInfo taskInfo = ParseTaskInfo.fromStatus(queryTask(taskId));
            if ("completed".equals(taskStatusValue)) {
                taskInfo.setResult(buildResponse());
            }
            return taskInfo;
        }

        private ParseResponse buildResponse() {
            ParseResult result = new ParseResult();
            result.setFileName("demo.pdf");
            result.setMarkdown("# demo");
            result.setPlainText("demo");
            ParseResponse response = new ParseResponse();
            response.setResults(Collections.singletonList(result));
            return response;
        }
    }

    private static class FakeDefaultDocumentParseService implements DocumentParseService<ParseRequest> {

        private ParseRequest lastParseRequest;
        private int parseCallCount;

        @Override
        public ParseResponse parse(ParseRequest request) {
            parseCallCount++;
            lastParseRequest = request;
            ParseResult result = new ParseResult();
            result.setFileName("demo.docx");
            result.setMarkdown("# docx");
            result.setPlainText("docx");
            ParseResponse response = new ParseResponse();
            response.setResults(Collections.singletonList(result));
            return response;
        }

        @Override
        public ParseTaskStatus submit(ParseRequest request) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ParseTaskStatus queryTask(String taskId) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ParseResponse queryResult(String taskId) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ParseTaskInfo queryTaskInfo(String taskId) {
            throw new UnsupportedOperationException();
        }
    }

    private static class FakePptxDocumentParseService implements PptxDocumentParseService {

        private int parseCallCount;

        @Override
        public ParseResponse parse(ParseRequest request) {
            parseCallCount++;
            ParseResult result = new ParseResult();
            result.setFileName("slides.pptx");
            result.setMarkdown("# pptx");
            result.setPlainText("pptx");
            ParseResponse response = new ParseResponse();
            response.setResults(Collections.singletonList(result));
            return response;
        }

        @Override
        public ParseTaskStatus submit(ParseRequest request) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ParseTaskStatus queryTask(String taskId) {
            throw new UnsupportedOperationException();
        }

        @Override
        public ParseResponse queryResult(String taskId) {
            throw new UnsupportedOperationException();
        }
    }
}
