package tech.easyflow.skill.gitimport;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Skill Git 仓库导入的网络与资源边界配置。
 */
@Configuration
@ConfigurationProperties(prefix = "easyflow.skill.git-import")
public class SkillGitImportProperties {

    /** Git 网络操作超时时间。 */
    private Duration timeout = Duration.ofSeconds(45);

    /** 单次 Git 远程操作总超时时间。 */
    private Duration operationTimeout = Duration.ofSeconds(90);

    /** 单实例允许同时执行的 Git 远程操作数量。 */
    private int maxConcurrentOperations = 4;

    /** 同一账号两次仓库扫描之间的最小间隔。 */
    private Duration scanMinimumInterval = Duration.ofSeconds(2);

    /** 扫描令牌有效期。 */
    private Duration scanTtl = Duration.ofMinutes(30);

    /** 临时裸仓库最大磁盘占用。 */
    private long maxRepositoryBytes = 256L * 1024 * 1024;

    /** 单仓库最大文件对象数量。 */
    private int maxRepositoryFiles = 50_000;

    /** 单仓库最大 Skill 候选数量。 */
    private int maxCandidates = 2_000;

    /** 允许访问的 HTTPS 端口。 */
    private Set<Integer> allowedPorts = new LinkedHashSet<>(Set.of(443));

    /** 允许解析到私网地址的可信 Git 主机。 */
    private Set<String> trustedPrivateHosts = new LinkedHashSet<>();

    /**
     * 获取 Git 网络操作超时时间。
     *
     * @return 超时时间
     */
    public Duration getTimeout() {
        return timeout;
    }

    /**
     * 设置 Git 网络操作超时时间。
     *
     * @param timeout 超时时间
     */
    public void setTimeout(Duration timeout) {
        this.timeout = timeout == null ? Duration.ofSeconds(45) : timeout;
    }

    /**
     * 获取单次 Git 远程操作总超时时间。
     *
     * @return 总超时时间
     */
    public Duration getOperationTimeout() {
        return operationTimeout;
    }

    /**
     * 设置单次 Git 远程操作总超时时间。
     *
     * @param operationTimeout 总超时时间
     */
    public void setOperationTimeout(Duration operationTimeout) {
        this.operationTimeout = positive(operationTimeout, "operationTimeout");
    }

    /**
     * 获取单实例 Git 远程操作并发上限。
     *
     * @return 并发上限
     */
    public int getMaxConcurrentOperations() {
        return maxConcurrentOperations;
    }

    /**
     * 设置单实例 Git 远程操作并发上限。
     *
     * @param maxConcurrentOperations 并发上限
     */
    public void setMaxConcurrentOperations(int maxConcurrentOperations) {
        this.maxConcurrentOperations = positive(maxConcurrentOperations, "maxConcurrentOperations");
    }

    /**
     * 获取同一账号两次仓库扫描的最小间隔。
     *
     * @return 最小间隔
     */
    public Duration getScanMinimumInterval() {
        return scanMinimumInterval;
    }

    /**
     * 设置同一账号两次仓库扫描的最小间隔。
     *
     * @param scanMinimumInterval 最小间隔
     */
    public void setScanMinimumInterval(Duration scanMinimumInterval) {
        this.scanMinimumInterval = positive(scanMinimumInterval, "scanMinimumInterval");
    }

    /**
     * 获取扫描令牌有效期。
     *
     * @return 有效期
     */
    public Duration getScanTtl() {
        return scanTtl;
    }

    /**
     * 设置扫描令牌有效期。
     *
     * @param scanTtl 有效期
     */
    public void setScanTtl(Duration scanTtl) {
        this.scanTtl = scanTtl == null ? Duration.ofMinutes(30) : scanTtl;
    }

    /**
     * 获取临时仓库大小上限。
     *
     * @return 字节数
     */
    public long getMaxRepositoryBytes() {
        return maxRepositoryBytes;
    }

    /**
     * 设置临时仓库大小上限。
     *
     * @param maxRepositoryBytes 字节数
     */
    public void setMaxRepositoryBytes(long maxRepositoryBytes) {
        this.maxRepositoryBytes = positive(maxRepositoryBytes, "maxRepositoryBytes");
    }

    /**
     * 获取仓库文件对象数量上限。
     *
     * @return 文件对象数量
     */
    public int getMaxRepositoryFiles() {
        return maxRepositoryFiles;
    }

    /**
     * 设置仓库文件对象数量上限。
     *
     * @param maxRepositoryFiles 文件对象数量
     */
    public void setMaxRepositoryFiles(int maxRepositoryFiles) {
        this.maxRepositoryFiles = positive(maxRepositoryFiles, "maxRepositoryFiles");
    }

    /**
     * 获取 Skill 候选数量上限。
     *
     * @return 候选数量
     */
    public int getMaxCandidates() {
        return maxCandidates;
    }

    /**
     * 设置 Skill 候选数量上限。
     *
     * @param maxCandidates 候选数量
     */
    public void setMaxCandidates(int maxCandidates) {
        this.maxCandidates = positive(maxCandidates, "maxCandidates");
    }

    /**
     * 获取允许访问的 HTTPS 端口。
     *
     * @return 端口集合
     */
    public Set<Integer> getAllowedPorts() {
        return allowedPorts;
    }

    /**
     * 设置允许访问的 HTTPS 端口。
     *
     * @param allowedPorts 端口集合
     */
    public void setAllowedPorts(Set<Integer> allowedPorts) {
        LinkedHashSet<Integer> normalized = new LinkedHashSet<>();
        if (allowedPorts != null) {
            allowedPorts.stream().filter(port -> port != null && port > 0 && port <= 65_535)
                    .forEach(normalized::add);
        }
        this.allowedPorts = normalized.isEmpty() ? new LinkedHashSet<>(Set.of(443)) : normalized;
    }

    /**
     * 获取可信私网 Git 主机。
     *
     * @return 小写主机集合
     */
    public Set<String> getTrustedPrivateHosts() {
        return trustedPrivateHosts;
    }

    /**
     * 设置可信私网 Git 主机。
     *
     * @param trustedPrivateHosts 主机集合
     */
    public void setTrustedPrivateHosts(Set<String> trustedPrivateHosts) {
        LinkedHashSet<String> normalized = new LinkedHashSet<>();
        if (trustedPrivateHosts != null) {
            trustedPrivateHosts.stream().filter(host -> host != null && !host.isBlank())
                    .map(host -> host.trim().toLowerCase(java.util.Locale.ROOT))
                    .forEach(normalized::add);
        }
        this.trustedPrivateHosts = normalized;
    }

    private static int positive(int value, String name) {
        if (value <= 0) {
            throw new IllegalArgumentException(name + " must be positive");
        }
        return value;
    }

    private static long positive(long value, String name) {
        if (value <= 0) {
            throw new IllegalArgumentException(name + " must be positive");
        }
        return value;
    }

    private static Duration positive(Duration value, String name) {
        if (value == null || value.isZero() || value.isNegative()) {
            throw new IllegalArgumentException(name + " must be positive");
        }
        return value;
    }
}
