/*
 *  Copyright (c) 2023-2026, Easy-Agents (fuhai999@gmail.com).
 *  <p>
 *  Licensed under the Apache License, Version 2.0.
 */
package com.easyagents.core.file2text;

import com.easyagents.core.file2text.source.DocumentSource;

import java.util.List;

/**
 * 结构化文档读取结果构造工具。
 */
public final class DocumentReadSupport {

    private DocumentReadSupport() {
    }

    /**
     * 创建带定位的文本片段。
     *
     * @param segmentId 片段 ID
     * @param text 文本
     * @param locatorType 定位类型
     * @param locatorLabel 定位标签
     * @param startIndex 起始字符下标
     * @param headingPath 标题路径
     * @return 文本片段
     */
    public static DocumentTextSegment segment(String segmentId,
                                              String text,
                                              String locatorType,
                                              String locatorLabel,
                                              int startIndex,
                                              List<String> headingPath) {
        String safeText = text == null ? "" : text.trim();
        DocumentTextSegment segment = new DocumentTextSegment();
        segment.setSegmentId(segmentId);
        segment.setText(safeText);
        segment.setLocatorType(locatorType);
        segment.setLocatorLabel(locatorLabel);
        segment.setStartIndex(startIndex);
        segment.setEndIndex(startIndex + safeText.length());
        segment.setHeadingPath(headingPath);
        segment.setTokenEstimate(estimateTokens(safeText));
        return segment;
    }

    /**
     * 汇总结构化读取结果。
     *
     * @param source 文档来源
     * @param segments 片段
     * @param request 读取请求
     * @return 读取结果
     * @throws DocumentReadException 结果为空或超过边界
     */
    public static LightweightDocumentReadResult result(DocumentSource source,
                                                       List<DocumentTextSegment> segments,
                                                       LightweightDocumentReadRequest request)
            throws DocumentReadException {
        List<DocumentTextSegment> nonEmpty = segments.stream()
                .filter(item -> item.getText() != null && !item.getText().isBlank())
                .toList();
        if (nonEmpty.isEmpty()) {
            throw new DocumentReadException(DocumentReadErrorCode.DOCUMENT_NO_READABLE_TEXT,
                    "No readable text detected");
        }
        long charCount = nonEmpty.stream().mapToLong(item -> item.getText().length()).sum()
                + Math.max(0, nonEmpty.size() - 1L);
        if (charCount > request.getMaxExpandedChars() || charCount > Integer.MAX_VALUE) {
            throw new DocumentReadException(DocumentReadErrorCode.DOCUMENT_STRUCTURE_LIMIT_EXCEEDED,
                    "Expanded document text exceeds the configured limit");
        }
        LightweightDocumentReadResult result = new LightweightDocumentReadResult();
        result.setFileName(source.getFileName());
        result.setMimeType(source.getMimeType());
        result.setSegments(nonEmpty);
        result.setCharCount((int) charCount);
        result.setTokenEstimate(nonEmpty.stream().mapToInt(DocumentTextSegment::getTokenEstimate).sum());
        return result;
    }

    /**
     * 使用偏保守的字符规则估算 Token 数。
     *
     * @param text 文本
     * @return Token 估算
     */
    public static int estimateTokens(String text) {
        if (text == null || text.isEmpty()) {
            return 0;
        }
        double tokens = 0;
        for (int offset = 0; offset < text.length();) {
            int codePoint = text.codePointAt(offset);
            Character.UnicodeScript script = Character.UnicodeScript.of(codePoint);
            tokens += switch (script) {
                case HAN, HANGUL, HIRAGANA, KATAKANA -> 1.0d;
                default -> Character.isWhitespace(codePoint) ? 0.1d : 0.25d;
            };
            offset += Character.charCount(codePoint);
        }
        return Math.max(1, (int) Math.ceil(tokens));
    }
}
