package at.procon.dip.normalization.impl;

import at.procon.dip.domain.document.ContentRole;
import at.procon.dip.domain.document.DocumentType;
import at.procon.dip.domain.document.RepresentationType;
import at.procon.dip.normalization.spi.RepresentationBuildRequest;
import at.procon.dip.normalization.spi.TextRepresentationBuilder;
import at.procon.dip.normalization.spi.TextRepresentationDraft;
import at.procon.dip.search.config.DipSearchProperties;
import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
import at.procon.dip.runtime.config.RuntimeMode;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@Order(200)
@RequiredArgsConstructor
public class ChunkedLongTextRepresentationBuilder implements TextRepresentationBuilder {

    public static final String BUILDER_KEY = "long-text-chunker";

    private final DipSearchProperties properties;

    @Override
    public boolean supports(DocumentType documentType) {
        return true;
    }

    @Override
    public List<TextRepresentationDraft> build(RepresentationBuildRequest request) {
        if (!properties.isChunkingEnabled()) {
            return List.of();
        }

        String baseText = request.extractionResult().derivedTextByRole().get(ContentRole.NORMALIZED_TEXT);
        if (!StringUtils.hasText(baseText)) {
            baseText = request.extractionResult().derivedTextByRole().get(ContentRole.HTML_CLEAN);
        }
        if (!StringUtils.hasText(baseText)) {
            return List.of();
        }

        int target = Math.max(400, properties.getChunkTargetChars());
        int overlap = Math.max(0, Math.min(target / 3, properties.getChunkOverlapChars()));
        if (baseText.length() <= target + overlap) {
            return List.of();
        }

        List<TextRepresentationDraft> drafts = new ArrayList<>();
        int start = 0;
        int chunkIndex = 0;
        while (start < baseText.length() && chunkIndex < properties.getMaxChunksPerDocument()) {
            int end = Math.min(baseText.length(), start + target);
            if (end < baseText.length()) {
                int boundary = findBoundary(baseText, end, Math.min(baseText.length(), end + 160));
                if (boundary > start + 200) {
                    end = boundary;
                }
            }

            String chunk = baseText.substring(start, end).trim();
            if (StringUtils.hasText(chunk)) {
                drafts.add(new TextRepresentationDraft(
                        RepresentationType.CHUNK,
                        BUILDER_KEY,
                        request.detectionResult().languageCode(),
                        chunk,
                        false,
                        chunkIndex,
                        start,
                        end,
                        ContentRole.NORMALIZED_TEXT,
                        Boolean.TRUE
                ));
                chunkIndex++;
            }

            if (end >= baseText.length()) {
                break;
            }
            start = Math.max(end - overlap, start + 1);
        }
        return drafts;
    }

    private int findBoundary(String text, int preferred, int max) {
        for (int i = preferred; i < max; i++) {
            char c = text.charAt(i);
            if (c == '\n' || c == '.' || c == '!' || c == '?' || c == ';') {
                return i + 1;
            }
        }
        return preferred;
    }
}