package tech.easyflow.agent.config;

import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;
import org.springframework.boot.convert.DataSizeUnit;
import org.springframework.util.unit.DataSize;
import org.springframework.util.unit.DataUnit;
import org.springframework.validation.annotation.Validated;

import java.time.Duration;

/**
 * Agent 文档附件的 I/O、并发与格式安全边界配置。
 */
@Validated
@ConfigurationProperties(prefix = "easyflow.agent.document")
public class AgentDocumentProperties {

    /** 是否启用文档附件。 */
    private boolean enabled = true;
    /** 单轮最大文档数。 */
    @Min(1)
    private int maxDocumentsPerTurn = 3;
    /** 单轮文档总字节上限。 */
    @NotNull
    @DataSizeUnit(DataUnit.MEGABYTES)
    private DataSize maxTotalBytesPerTurn = DataSize.ofMegabytes(30);
    /** 单文档读取超时。 */
    @NotNull
    private Duration readTimeout = Duration.ofSeconds(30);
    /** 未绑定文档保留时间。 */
    @NotNull
    private Duration tempRetention = Duration.ofHours(24);
    /** 读取线程池配置。 */
    @Valid
    @NotNull
    private Reader reader = new Reader();
    /** 格式安全边界。 */
    @Valid
    @NotNull
    private Limits limits = new Limits();

    /** @return 是否启用文档附件 */
    public boolean isEnabled() { return enabled; }
    /** @param enabled 是否启用文档附件 */
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    /** @return 单轮最大文档数 */
    public int getMaxDocumentsPerTurn() { return maxDocumentsPerTurn; }
    /** @param maxDocumentsPerTurn 单轮最大文档数 */
    public void setMaxDocumentsPerTurn(int maxDocumentsPerTurn) { this.maxDocumentsPerTurn = maxDocumentsPerTurn; }
    /** @return 单轮文档总大小 */
    public DataSize getMaxTotalBytesPerTurn() { return maxTotalBytesPerTurn; }
    /** @param maxTotalBytesPerTurn 单轮文档总大小 */
    public void setMaxTotalBytesPerTurn(DataSize maxTotalBytesPerTurn) {
        this.maxTotalBytesPerTurn = maxTotalBytesPerTurn;
    }
    /** @return 读取超时 */
    public Duration getReadTimeout() { return readTimeout; }
    /** @param readTimeout 读取超时 */
    public void setReadTimeout(Duration readTimeout) { this.readTimeout = readTimeout; }
    /** @return 临时附件保留时间 */
    public Duration getTempRetention() { return tempRetention; }
    /** @param tempRetention 临时附件保留时间 */
    public void setTempRetention(Duration tempRetention) { this.tempRetention = tempRetention; }
    /** @return 读取线程池配置 */
    public Reader getReader() { return reader; }
    /** @param reader 读取线程池配置 */
    public void setReader(Reader reader) { this.reader = reader; }
    /** @return 格式安全边界 */
    public Limits getLimits() { return limits; }
    /** @param limits 格式安全边界 */
    public void setLimits(Limits limits) { this.limits = limits; }

    /**
     * 校验持续时间和数据大小均为正值。
     *
     * @return 配置是否合法
     */
    @AssertTrue(message = "文档大小与时间配置必须大于 0")
    public boolean isPositiveBoundaries() {
        return maxTotalBytesPerTurn != null && maxTotalBytesPerTurn.toBytes() > 0
                && readTimeout != null && !readTimeout.isZero() && !readTimeout.isNegative()
                && tempRetention != null && !tempRetention.isZero() && !tempRetention.isNegative();
    }

    /**
     * 文档读取线程池配置。
     */
    public static class Reader {

        /** 核心线程数。 */
        @Min(1)
        private int coreSize = 2;
        /** 最大线程数。 */
        @Min(1)
        private int maxSize = 4;
        /** 有界队列容量。 */
        @Min(1)
        private int queueCapacity = 32;

        /** @return 核心线程数 */
        public int getCoreSize() { return coreSize; }
        /** @param coreSize 核心线程数 */
        public void setCoreSize(int coreSize) { this.coreSize = coreSize; }
        /** @return 最大线程数 */
        public int getMaxSize() { return maxSize; }
        /** @param maxSize 最大线程数 */
        public void setMaxSize(int maxSize) { this.maxSize = maxSize; }
        /** @return 队列容量 */
        public int getQueueCapacity() { return queueCapacity; }
        /** @param queueCapacity 队列容量 */
        public void setQueueCapacity(int queueCapacity) { this.queueCapacity = queueCapacity; }

        /**
         * 校验最大线程数不小于核心线程数。
         *
         * @return 配置是否合法
         */
        @AssertTrue(message = "文档读取最大线程数不能小于核心线程数")
        public boolean isThreadRangeValid() {
            return maxSize >= coreSize;
        }
    }

    /**
     * 各文档格式的安全边界。
     */
    public static class Limits {

        /** Office 单文件上限。 */
        @NotNull
        @DataSizeUnit(DataUnit.MEGABYTES)
        private DataSize officeMaxBytes = DataSize.ofMegabytes(20);
        /** Excel 单文件上限。 */
        @NotNull
        @DataSizeUnit(DataUnit.MEGABYTES)
        private DataSize excelMaxBytes = DataSize.ofMegabytes(10);
        /** 文本单文件上限。 */
        @NotNull
        @DataSizeUnit(DataUnit.MEGABYTES)
        private DataSize textMaxBytes = DataSize.ofMegabytes(5);
        /** PDF 最大页数。 */
        @Min(1)
        private int maxPdfPages = 200;
        /** 演示文稿最大幻灯片数。 */
        @Min(1)
        private int maxSlides = 200;
        /** 表格最大工作表数。 */
        @Min(1)
        private int maxSheets = 20;
        /** 表格最大非空单元格数。 */
        @Min(1)
        private int maxNonEmptyCells = 50_000;
        /** 最大展开内容量。 */
        @NotNull
        @DataSizeUnit(DataUnit.MEGABYTES)
        private DataSize maxExpandedBytes = DataSize.ofMegabytes(150);

        /** @return Office 单文件上限 */
        public DataSize getOfficeMaxBytes() { return officeMaxBytes; }
        /** @param officeMaxBytes Office 单文件上限 */
        public void setOfficeMaxBytes(DataSize officeMaxBytes) { this.officeMaxBytes = officeMaxBytes; }
        /** @return Excel 单文件上限 */
        public DataSize getExcelMaxBytes() { return excelMaxBytes; }
        /** @param excelMaxBytes Excel 单文件上限 */
        public void setExcelMaxBytes(DataSize excelMaxBytes) { this.excelMaxBytes = excelMaxBytes; }
        /** @return 文本单文件上限 */
        public DataSize getTextMaxBytes() { return textMaxBytes; }
        /** @param textMaxBytes 文本单文件上限 */
        public void setTextMaxBytes(DataSize textMaxBytes) { this.textMaxBytes = textMaxBytes; }
        /** @return 最大 PDF 页数 */
        public int getMaxPdfPages() { return maxPdfPages; }
        /** @param maxPdfPages 最大 PDF 页数 */
        public void setMaxPdfPages(int maxPdfPages) { this.maxPdfPages = maxPdfPages; }
        /** @return 最大幻灯片数 */
        public int getMaxSlides() { return maxSlides; }
        /** @param maxSlides 最大幻灯片数 */
        public void setMaxSlides(int maxSlides) { this.maxSlides = maxSlides; }
        /** @return 最大工作表数 */
        public int getMaxSheets() { return maxSheets; }
        /** @param maxSheets 最大工作表数 */
        public void setMaxSheets(int maxSheets) { this.maxSheets = maxSheets; }
        /** @return 最大非空单元格数 */
        public int getMaxNonEmptyCells() { return maxNonEmptyCells; }
        /** @param maxNonEmptyCells 最大非空单元格数 */
        public void setMaxNonEmptyCells(int maxNonEmptyCells) { this.maxNonEmptyCells = maxNonEmptyCells; }
        /** @return 最大展开内容量 */
        public DataSize getMaxExpandedBytes() { return maxExpandedBytes; }
        /** @param maxExpandedBytes 最大展开内容量 */
        public void setMaxExpandedBytes(DataSize maxExpandedBytes) { this.maxExpandedBytes = maxExpandedBytes; }

        /**
         * 校验所有数据大小为正值。
         *
         * @return 配置是否合法
         */
        @AssertTrue(message = "文档格式大小上限必须大于 0")
        public boolean isPositiveSizes() {
            return positive(officeMaxBytes) && positive(excelMaxBytes)
                    && positive(textMaxBytes) && positive(maxExpandedBytes);
        }

        private boolean positive(DataSize value) {
            return value != null && value.toBytes() > 0;
        }
    }
}
