package com.agent.management.engine;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Pattern;

/**
 * 工作流运行目录管理器
 * 为每次工作流执行创建独立的工作目录，永久保留
 *
 * <p>目录结构：
 * <ul>
 *   <li>新建：{@code {basePath}/{workflowName}/{runId}/}（workflowName 为 kebab-case 唯一标识）</li>
 *   <li>兼容：旧目录 {@code {basePath}/{workflowId}/{runId}/} 仍可读取（用户改名前的历史 run）</li>
 * </ul>
 */
@Slf4j
@Component
public class WorkflowRunDirManager {

    /** runId 白名单：UUID 截取的 8~16 位十六进制 */
    private static final Pattern RUN_ID_PATTERN = Pattern.compile("^[a-fA-F0-9]{1,16}$");

    private final Path basePath;

    public WorkflowRunDirManager(
            @Value("${workflow.run-dir:./data/workflow-runs}") String basePath) {
        this.basePath = Paths.get(basePath).toAbsolutePath().normalize();
        try {
            Files.createDirectories(this.basePath);
            log.info("[RunDirManager] 工作目录基路径: {}", this.basePath);
        } catch (IOException e) {
            throw new IllegalStateException("无法创建工作流运行目录: " + this.basePath, e);
        }
    }

    /**
     * 为指定工作流执行创建工作目录（新建使用 workflowName 作为目录段）。
     *
     * @param workflowId   工作流内部 ID（兼容旧目录兜底用）
     * @param workflowName 工作流唯一标识（kebab-case），新建目录使用此字段
     * @param runId        运行 ID（必须为 1~16 位十六进制）
     */
    public Path createRunDir(Long workflowId, String workflowName, String runId) {
        validateRunId(runId);
        Path runDir = resolveByName(workflowName, runId);
        try {
            Files.createDirectories(runDir);
            log.info("[RunDirManager] 创建运行目录: {}", runDir);
            return runDir;
        } catch (IOException e) {
            throw new RuntimeException("创建运行目录失败: " + runDir, e);
        }
    }

    /**
     * 获取指定运行的工作目录（已存在时返回，不存在返回 null）。
     * 查询顺序：先按 name，再按 id 兜底。
     */
    public Path getRunDir(Long workflowId, String workflowName, String runId) {
        try {
            validateRunId(runId);
        } catch (IllegalArgumentException e) {
            return null;
        }
        // 1. 优先按 name 查找
        if (workflowName != null && !workflowName.isEmpty()) {
            Path byName = resolveByName(workflowName, runId);
            if (Files.isDirectory(byName)) return byName;
        }
        // 2. 回退按 id 查找（兼容旧目录）
        if (workflowId != null) {
            Path byId = resolveById(workflowId, runId);
            if (Files.isDirectory(byId)) return byId;
        }
        return null;
    }

    /**
     * 兼容旧调用：仅按 id 创建/查询。新代码应使用带 name 的重载。
     */
    @Deprecated
    public Path createRunDir(Long workflowId, String runId) {
        return createRunDir(workflowId, String.valueOf(workflowId), runId);
    }

    @Deprecated
    public Path getRunDir(Long workflowId, String runId) {
        return getRunDir(workflowId, null, runId);
    }

    /**
     * 获取基路径
     */
    public Path getBasePath() {
        return basePath;
    }

    /**
     * 校验 runId 格式，防止路径穿越
     */
    private void validateRunId(String runId) {
        if (runId == null || !RUN_ID_PATTERN.matcher(runId).matches()) {
            throw new IllegalArgumentException("非法 runId: " + runId);
        }
    }

    private Path resolveByName(String workflowName, String runId) {
        Path runDir = basePath.resolve(workflowName).resolve(runId).normalize();
        if (!runDir.startsWith(basePath)) {
            throw new IllegalArgumentException("解析后的 runId 路径越界: " + runDir);
        }
        return runDir;
    }

    private Path resolveById(Long workflowId, String runId) {
        Path runDir = basePath.resolve(String.valueOf(workflowId)).resolve(runId).normalize();
        if (!runDir.startsWith(basePath)) {
            throw new IllegalArgumentException("解析后的 runId 路径越界: " + runDir);
        }
        return runDir;
    }
}
