package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.engine.NodeTypeUtils;
import com.agent.management.model.vo.WorkflowVersionVO;
import com.agent.management.service.WorkflowVersionService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class WorkflowVersionServiceImpl implements WorkflowVersionService {

    @Value("${workflow.run-dir:./data/workflow-runs}")
    private String runDir;

    private static final String VERSIONS_DIR = "_versions";
    private static final String VERSION_META_FILE = "_version.json";
    private static final String SNAPSHOT_FILE = "workflow.json";
    private static final ObjectMapper jsonMapper = new ObjectMapper()
            .enable(SerializationFeature.INDENT_OUTPUT);

    // ==================== 公有方法 ====================

    @Override
    public int createSnapshot(Long workflowId, String workflowName, String graphData, String snapshotName, String message) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        Path versionsDir = versionBase.resolve(VERSIONS_DIR);

        try {
            Files.createDirectories(versionsDir);
        } catch (IOException e) {
            throw new BusinessException("创建版本目录失败");
        }

        VersionMeta meta = loadMeta(versionBase);
        int maxVersion = meta.versions.stream()
                .mapToInt(e -> e.number)
                .max().orElse(0);
        int newVersion = maxVersion + 1;

        // 写入快照文件
        Path snapshotDir = versionsDir.resolve("v" + newVersion);
        try {
            Files.createDirectories(snapshotDir);
            SnapshotData snapshot = new SnapshotData();
            snapshot.graphData = NodeTypeUtils.normalizeGraphData(graphData);
            snapshot.name = snapshotName;
            snapshot.savedAt = LocalDateTime.now().toString();
            jsonMapper.writeValue(snapshotDir.resolve(SNAPSHOT_FILE).toFile(), snapshot);
        } catch (IOException e) {
            throw new BusinessException("写入快照失败");
        }

        // 更新元数据
        VersionEntry entry = new VersionEntry();
        entry.number = newVersion;
        entry.createdAt = LocalDateTime.now().toString();
        entry.message = message != null ? message : "";
        meta.versions.add(entry);
        meta.currentVersion = newVersion;
        saveMeta(versionBase, meta);

        log.info("Workflow [id={}, name={}] 创建快照 v{}", workflowId, versionBase.getFileName(), newVersion);
        return newVersion;
    }

    @Override
    public int getCurrentVersion(Long workflowId, String workflowName) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        return loadMeta(versionBase).currentVersion;
    }

    @Override
    public List<WorkflowVersionVO> listVersions(Long workflowId, String workflowName) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        VersionMeta meta = loadMeta(versionBase);

        List<WorkflowVersionVO> result = new ArrayList<>();
        for (VersionEntry entry : meta.versions) {
            WorkflowVersionVO vo = new WorkflowVersionVO();
            vo.setNumber(entry.number);
            vo.setCreatedAt(LocalDateTime.parse(entry.createdAt));
            vo.setMessage(entry.message);
            vo.setCurrent(entry.number == meta.currentVersion);
            result.add(vo);
        }
        return result;
    }

    @Override
    public String getVersionContent(Long workflowId, String workflowName, int version) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        validateVersionExists(versionBase, version);

        Path snapshotFile = versionBase.resolve(VERSIONS_DIR)
                .resolve("v" + version)
                .resolve(SNAPSHOT_FILE);
        if (!Files.exists(snapshotFile)) {
            throw new BusinessException(404, "版本 v" + version + " 快照文件不存在");
        }
        try {
            return Files.readString(snapshotFile, StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new BusinessException("读取版本内容失败");
        }
    }

    @Override
    public void rollback(Long workflowId, String workflowName, int version) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        VersionMeta meta = loadMeta(versionBase);
        validateVersionExists(versionBase, version);

        meta.currentVersion = version;
        saveMeta(versionBase, meta);

        log.info("Workflow [id={}, name={}] 回滚到 v{}", workflowId, versionBase.getFileName(), version);
    }

    @Override
    public void updateVersionMessage(Long workflowId, String workflowName, int version, String message) {
        Path versionBase = getVersionBase(workflowId, workflowName);
        VersionMeta meta = loadMeta(versionBase);

        VersionEntry entry = meta.versions.stream()
                .filter(e -> e.number == version)
                .findFirst()
                .orElseThrow(() -> new BusinessException(404, "版本不存在: v" + version));

        entry.message = message != null ? message : "";
        saveMeta(versionBase, meta);
    }

    // ==================== 私有方法 ====================

    /**
     * 获取工作流版本的根目录（双轨制）。
     *
     * <p>解析顺序（方案C Q2 选项1）：
     * <ol>
     *   <li>若 id 目录已存在 → 沿用 id 目录（保持历史快照一致）</li>
     *   <li>否则若 name 目录已存在 → 使用 name 目录</li>
     *   <li>否则用 name 创建新目录</li>
     *   <li>name 为空时退化为 id 目录</li>
     * </ol>
     */
    private Path getVersionBase(Long workflowId, String workflowName) {
        Path versionRoot = getVersionRoot();
        Path idBase = versionRoot.resolve(String.valueOf(workflowId));
        Path nameBase = (workflowName != null && !workflowName.isBlank())
                ? versionRoot.resolve(workflowName) : null;

        Path versionBase;
        if (Files.isDirectory(idBase)) {
            // 旧目录已存在，继续沿用，避免快照历史被分裂
            versionBase = idBase;
        } else if (nameBase != null && Files.isDirectory(nameBase)) {
            versionBase = nameBase;
        } else if (nameBase != null) {
            versionBase = nameBase;
        } else {
            versionBase = idBase;
        }

        try {
            Files.createDirectories(versionBase);
        } catch (IOException e) {
            throw new BusinessException("创建版本目录失败");
        }
        return versionBase;
    }

    private Path getVersionRoot() {
        Path runPath = Paths.get(runDir).toAbsolutePath();
        return runPath.getParent().resolve("workflow-versions");
    }

    private void validateVersionExists(Path versionBase, int version) {
        Path vDir = versionBase.resolve(VERSIONS_DIR).resolve("v" + version);
        if (!Files.exists(vDir)) {
            throw new BusinessException(404, "版本不存在: v" + version);
        }
    }

    // ==================== 元数据读写 ====================

    private VersionMeta loadMeta(Path versionBase) {
        Path metaFile = versionBase.resolve(VERSION_META_FILE);
        if (!Files.exists(metaFile)) {
            return new VersionMeta();
        }
        try {
            return jsonMapper.readValue(metaFile.toFile(), VersionMeta.class);
        } catch (IOException e) {
            log.warn("解析版本元数据失败，使用默认值: {}", metaFile, e);
            return new VersionMeta();
        }
    }

    private void saveMeta(Path versionBase, VersionMeta meta) {
        Path metaFile = versionBase.resolve(VERSION_META_FILE);
        try {
            jsonMapper.writeValue(metaFile.toFile(), meta);
        } catch (IOException e) {
            throw new BusinessException("保存版本元数据失败");
        }
    }

    // ==================== 内部数据结构 ====================

    @lombok.Data
    public static class VersionMeta {
        public int currentVersion = 0;
        public List<VersionEntry> versions = new ArrayList<>();
    }

    @lombok.Data
    public static class VersionEntry {
        public int number;
        public String createdAt;
        public String message;
    }

    /** 快照数据结构 */
    @lombok.Data
    public static class SnapshotData {
        public String graphData;
        public String name;
        public String savedAt;
    }
}
