/*
 *  Copyright (c) 2023-2026, Easy-Agents (fuhai999@gmail.com).
 *  <p>
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *  <p>
 *  http://www.apache.org/licenses/LICENSE-2.0
 *  <p>
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.easyagents.core.file2text;


import com.easyagents.core.file2text.extractor.FileExtractor;
import com.easyagents.core.file2text.extractor.ExtractorRegistry;
import com.easyagents.core.file2text.source.*;

import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 文档轻量读取服务。
 */
public class File2TextService {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(File2TextService.class);
    private final ExtractorRegistry registry;

    public File2TextService() {
        this(new ExtractorRegistry());
    }

    public File2TextService(ExtractorRegistry registry) {
        this.registry = registry;
    }

    public ExtractorRegistry getRegistry() {
        return registry;
    }

    public String extractTextFromHttpUrl(String httpUrl) {
        return extractTextFromSource(new HttpDocumentSource(httpUrl));
    }

    public String extractTextFromHttpUrl(String httpUrl, String fileName) {
        return extractTextFromSource(new HttpDocumentSource(httpUrl, fileName));
    }

    public String extractTextFromHttpUrl(String httpUrl, String fileName, String mimeType) {
        return extractTextFromSource(new HttpDocumentSource(httpUrl, fileName, mimeType));
    }

    public String extractTextFromFile(File file) {
        return extractTextFromSource(new FileDocumentSource(file));
    }

    public String extractTextFromStream(InputStream is, String fileName, String mimeType) {
        return extractTextFromSource(new ByteStreamDocumentSource(is, fileName, mimeType));
    }

    public String extractTextFromBytes(byte[] bytes, String fileName, String mimeType) {
        return extractTextFromSource(new ByteArrayDocumentSource(bytes, fileName, mimeType));
    }


    /**
     * 从 DocumentSource 提取纯文本
     * 支持多 Extractor 降级重试
     *
     * @param source 文档输入源
     * @return 提取的文本（非空去空格），若无法提取则抛出异常
     * @throws IllegalArgumentException 输入源为空
     */
    public String extractTextFromSource(DocumentSource source) {
        return readFromSource(new LightweightDocumentReadRequest(source)).getText();
    }

    /**
     * 从文件读取结构化文档内容。
     *
     * @param file 文档文件
     * @return 结构化结果
     */
    public LightweightDocumentReadResult readFromFile(File file) {
        return readFromSource(new LightweightDocumentReadRequest(new FileDocumentSource(file)));
    }

    /**
     * 从输入流读取结构化文档内容。
     *
     * @param inputStream 文档输入流
     * @param fileName 文件名
     * @param mimeType MIME 类型
     * @return 结构化结果
     */
    public LightweightDocumentReadResult readFromStream(InputStream inputStream, String fileName, String mimeType) {
        return readFromSource(new LightweightDocumentReadRequest(
                new ByteStreamDocumentSource(inputStream, fileName, mimeType)));
    }

    /**
     * 按请求读取结构化文档内容。
     *
     * @param request 读取请求
     * @return 结构化结果
     * @throws DocumentReadException 不支持、空文本或读取失败
     */
    public LightweightDocumentReadResult readFromSource(LightweightDocumentReadRequest request) {
        if (request == null || request.getSource() == null) {
            throw new IllegalArgumentException("Document read request cannot be null");
        }
        DocumentSource source = request.getSource();
        if (source == null) {
            throw new IllegalArgumentException("DocumentSource cannot be null");
        }

        try {
            // 获取可用的 Extractor（按优先级排序）
            List<FileExtractor> candidates = registry.findExtractors(source);
            if (candidates.isEmpty()) {
                throw new DocumentReadException(DocumentReadErrorCode.UNSUPPORTED_DOCUMENT_TYPE,
                        "Unsupported document type: " + safeFileName(source));
            }

            // 日志：输出候选 Extractor
            log.info("Trying extractors for {}: {}", safeFileName(source),
                candidates.stream()
                    .map(e -> e.getClass().getSimpleName())
                    .collect(Collectors.joining(", ")));

            DocumentReadException lastFailure = null;
            for (FileExtractor extractor : candidates) {
                try {
                    log.debug("Trying {} on {}", extractor.getClass().getSimpleName(), safeFileName(source));
                    LightweightDocumentReadResult result = extractor.read(request);
                    if (result != null && !result.getSegments().isEmpty()) {
                        log.debug("Success with {}: extracted {} chars",
                            extractor.getClass().getSimpleName(), result.getCharCount());
                        return result;
                    }
                } catch (DocumentReadException e) {
                    lastFailure = e;
                    log.warn("Extractor {} rejected {} with {}: {}",
                            extractor.getClass().getSimpleName(), safeFileName(source),
                            e.getErrorCode(), e.getMessage());
                } catch (IOException e) {
                    lastFailure = new DocumentReadException(DocumentReadErrorCode.DOCUMENT_READ_FAILED,
                            "Failed to read document: " + safeFileName(source), e);
                    log.warn("Extractor {} failed on {}: {}",
                        extractor.getClass().getSimpleName(),
                        safeFileName(source),
                        e.toString());
                } catch (RuntimeException e) {
                    lastFailure = new DocumentReadException(DocumentReadErrorCode.DOCUMENT_READ_FAILED,
                            "Failed to read document: " + safeFileName(source), e);
                    log.warn("Extractor {} failed on {}: {}",
                            extractor.getClass().getSimpleName(), safeFileName(source), e.toString());
                }
            }

            if (lastFailure != null) {
                throw lastFailure;
            }
            throw new DocumentReadException(DocumentReadErrorCode.DOCUMENT_NO_READABLE_TEXT,
                    "No readable text detected: " + safeFileName(source));
        } finally {
            source.cleanup();
        }

    }

    private String safeFileName(DocumentSource source) {
        try {
            return source.getFileName() != null ? source.getFileName() : "unknown";
        } catch (Exception e) {
            return "unknown";
        }
    }
}
