package com.agent.management.service.impl;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.config.SkillProperties;
import com.agent.management.engine.hermes.HermesSkillSyncService;
import com.agent.management.model.vo.SkillVersionVO;
import com.agent.management.service.SkillVersionService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Stream;

@Slf4j
@Service
@RequiredArgsConstructor
public class SkillVersionServiceImpl implements SkillVersionService {

    private final SkillProperties skillProperties;
    private final HermesSkillSyncService hermesSkillSyncService;

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

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

    @Override
    public int createSnapshot(String folderName, String message) {
        Path skillDir = getSkillDir(folderName);
        Path versionsDir = skillDir.resolve(VERSIONS_DIR);

        VersionMeta meta = loadMeta(skillDir);
        // 版本号始终取历史最大值 + 1，保证单调递增（避免回滚后再保存版本号冲突）
        int maxVersion = meta.versions.stream()
                .mapToInt(e -> e.number)
                .max().orElse(0);
        int newVersion = maxVersion + 1;

        // 创建 v{n} 目录并复制工作区文件
        Path snapshotDir = versionsDir.resolve("v" + newVersion);
        copyWorkingDir(skillDir, snapshotDir);

        // 更新元数据
        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(skillDir, meta);

        log.info("Skill [{}] 创建快照 v{}", folderName, newVersion);
        return newVersion;
    }

    @Override
    public int getCurrentVersion(String folderName) {
        Path skillDir = getSkillDir(folderName);
        return loadMeta(skillDir).currentVersion;
    }

    @Override
    public List<SkillVersionVO> listVersions(String folderName) {
        Path skillDir = getSkillDir(folderName);
        VersionMeta meta = loadMeta(skillDir);

        List<SkillVersionVO> result = new ArrayList<>();
        for (VersionEntry entry : meta.versions) {
            SkillVersionVO vo = new SkillVersionVO();
            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(String folderName, int version) {
        Path skillDir = getSkillDir(folderName);
        validateVersionExists(skillDir, version);

        Path skillMd = skillDir.resolve(VERSIONS_DIR)
                .resolve("v" + version)
                .resolve("SKILL.md");
        if (!Files.exists(skillMd)) {
            throw new BusinessException(404, "版本 v" + version + " 无 SKILL.md");
        }
        try {
            return Files.readString(skillMd, StandardCharsets.UTF_8);
        } catch (IOException e) {
            throw new BusinessException("读取版本内容失败");
        }
    }

    @Override
    public void rollback(String folderName, int version) {
        Path skillDir = getSkillDir(folderName);
        VersionMeta meta = loadMeta(skillDir);
        validateVersionExists(skillDir, version);

        // 将快照内容恢复到工作区（排除 _versions 和 _version.json）
        Path snapshotDir = skillDir.resolve(VERSIONS_DIR).resolve("v" + version);
        restoreToWorkingDir(snapshotDir, skillDir);

        // 仅更新 currentVersion，不创建新快照
        meta.currentVersion = version;
        saveMeta(skillDir, meta);

        log.info("Skill [{}] 回滚到 v{}", folderName, version);
        hermesSkillSyncService.syncAfterWrite();
    }

    @Override
    public void updateVersionMessage(String folderName, int version, String message) {
        Path skillDir = getSkillDir(folderName);
        VersionMeta meta = loadMeta(skillDir);

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

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

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

    private Path getSkillDir(String folderName) {
        Path dir = Paths.get(skillProperties.getBasePath()).toAbsolutePath().resolve(folderName);
        if (!Files.exists(dir) || !Files.isDirectory(dir)) {
            throw new BusinessException(404, "Skill 不存在: " + folderName);
        }
        return dir;
    }

    /**
     * 复制工作区到快照目录（排除 _versions/ 和 _version.json）
     */
    private void copyWorkingDir(Path skillDir, Path snapshotDir) {
        try {
            Files.createDirectories(snapshotDir);
            try (Stream<Path> paths = Files.list(skillDir)) {
                paths.filter(p -> {
                    String name = p.getFileName().toString();
                    return !name.equals(VERSIONS_DIR) && !name.equals(VERSION_META_FILE);
                }).forEach(p -> {
                    Path target = snapshotDir.resolve(p.getFileName().toString());
                    try {
                        if (Files.isDirectory(p)) {
                            FileUtils.copyDirectory(p.toFile(), target.toFile());
                        } else {
                            Files.copy(p, target, StandardCopyOption.REPLACE_EXISTING);
                        }
                    } catch (IOException e) {
                        log.error("复制文件失败: {} -> {}", p, target, e);
                        throw new BusinessException("创建快照失败");
                    }
                });
            }
        } catch (IOException e) {
            throw new BusinessException("创建快照失败");
        }
    }

    /**
     * 将快照内容恢复到工作区（先清空工作区文件，再从快照复制）
     */
    private void restoreToWorkingDir(Path snapshotDir, Path skillDir) {
        try {
            // 删除工作区中现有的业务文件（排除 _versions/ 和 _version.json）
            try (Stream<Path> paths = Files.list(skillDir)) {
                paths.filter(p -> {
                    String name = p.getFileName().toString();
                    return !name.equals(VERSIONS_DIR) && !name.equals(VERSION_META_FILE);
                }).forEach(p -> {
                    try {
                        if (Files.isDirectory(p)) {
                            FileUtils.deleteDirectory(p.toFile());
                        } else {
                            Files.delete(p);
                        }
                    } catch (IOException e) {
                        log.error("删除工作区文件失败: {}", p, e);
                    }
                });
            }

            // 从快照复制到工作区
            try (Stream<Path> paths = Files.list(snapshotDir)) {
                paths.forEach(p -> {
                    Path target = skillDir.resolve(p.getFileName().toString());
                    try {
                        if (Files.isDirectory(p)) {
                            FileUtils.copyDirectory(p.toFile(), target.toFile());
                        } else {
                            Files.copy(p, target, StandardCopyOption.REPLACE_EXISTING);
                        }
                    } catch (IOException e) {
                        log.error("恢复文件失败: {} -> {}", p, target, e);
                        throw new BusinessException("回滚失败");
                    }
                });
            }
        } catch (IOException e) {
            throw new BusinessException("回滚失败");
        }
    }

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

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

    private VersionMeta loadMeta(Path skillDir) {
        Path metaFile = skillDir.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 skillDir, VersionMeta meta) {
        Path metaFile = skillDir.resolve(VERSION_META_FILE);
        try {
            jsonMapper.writeValue(metaFile.toFile(), meta);
        } catch (IOException e) {
            throw new BusinessException("保存版本元数据失败");
        }
    }

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

    /** _version.json 的数据结构 */
    @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;
    }
}
