using System.IO;
using System.IO.Compression;
using AxCopilot.Services;

namespace AxCopilot.Services.Agent;

public static partial class SkillService
{
    // ─── 가져오기/내보내기 ──────────────────────────────────────────────────

    /// <summary>
    /// 스킬을 zip 파일로 내보냅니다.
    /// zip 구조: skill-name/ 폴더 안에 .skill.md 파일 (+ SKILL.md 표준의 경우 전체 폴더).
    /// </summary>
    /// <returns>생성된 zip 파일 경로. 실패 시 null.</returns>
    public static string? ExportSkill(SkillDefinition skill, string outputDir)
    {
        try
        {
            if (!File.Exists(skill.FilePath))
            {
                LogService.Warn($"스킬 내보내기 실패: 파일 없음 — {skill.FilePath}");
                return null;
            }

            var zipName = $"{skill.Name}.skill.zip";
            var zipPath = Path.Combine(outputDir, zipName);

            // 기존 파일이 있으면 삭제
            if (File.Exists(zipPath)) File.Delete(zipPath);

            using var zip = ZipFile.Open(zipPath, ZipArchiveMode.Create);

            if (skill.IsStandardFormat)
            {
                // SKILL.md 표준: 전체 폴더를 zip에 추가
                var skillDir = Path.GetDirectoryName(skill.FilePath);
                if (skillDir != null && Directory.Exists(skillDir))
                {
                    var baseName = Path.GetFileName(skillDir);
                    foreach (var file in Directory.EnumerateFiles(skillDir, "*", SearchOption.AllDirectories))
                    {
                        // 실행 가능 파일 제외
                        var ext = Path.GetExtension(file).ToLowerInvariant();
                        if (ext is ".exe" or ".dll" or ".bat" or ".cmd" or ".ps1" or ".sh") continue;

                        var entryName = baseName + "/" + Path.GetRelativePath(skillDir, file).Replace('\\', '/');
                        zip.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
                    }
                }
            }
            else
            {
                // *.skill.md 파일 단독
                var entryName = $"{skill.Name}/{Path.GetFileName(skill.FilePath)}";
                zip.CreateEntryFromFile(skill.FilePath, entryName, CompressionLevel.Optimal);
            }

            LogService.Info($"스킬 내보내기 완료: {zipPath}");
            return zipPath;
        }
        catch (Exception ex)
        {
            LogService.Warn($"스킬 내보내기 실패: {ex.Message}");
            return null;
        }
    }

    /// <summary>
    /// zip 파일에서 스킬을 가져옵니다.
    /// zip 안의 .skill.md 또는 SKILL.md 파일을 사용자 스킬 폴더에 설치합니다.
    /// </summary>
    /// <returns>가져온 스킬 수. 0이면 실패.</returns>
    public static int ImportSkills(string zipPath)
    {
        try
        {
            if (!File.Exists(zipPath))
            {
                LogService.Warn($"스킬 가져오기 실패: 파일 없음 — {zipPath}");
                return 0;
            }

            var userFolder = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                "AxCopilot", "skills");
            if (!Directory.Exists(userFolder))
                Directory.CreateDirectory(userFolder);

            using var zip = ZipFile.OpenRead(zipPath);

            // 보안 검증: 실행 가능 파일 차단
            var dangerousExts = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
                { ".exe", ".dll", ".bat", ".cmd", ".ps1", ".sh", ".com", ".scr", ".msi" };
            foreach (var entry in zip.Entries)
            {
                if (dangerousExts.Contains(Path.GetExtension(entry.Name)))
                {
                    LogService.Warn($"스킬 가져오기 차단: 실행 가능 파일 포함 — {entry.FullName}");
                    return 0;
                }
            }

            // 스킬 파일 존재 여부 확인
            var skillEntries = zip.Entries
                .Where(e => e.Name.EndsWith(".skill.md", StringComparison.OrdinalIgnoreCase)
                         || e.Name.Equals("SKILL.md", StringComparison.OrdinalIgnoreCase))
                .ToList();
            if (skillEntries.Count == 0)
            {
                LogService.Warn("스킬 가져오기 실패: zip에 .skill.md 또는 SKILL.md 파일 없음");
                return 0;
            }

            // zip 압축 해제
            int importedCount = 0;
            foreach (var entry in zip.Entries)
            {
                if (string.IsNullOrEmpty(entry.Name)) continue; // 디렉토리 항목 건너뛰기

                // 상위 경로 이탈 방지
                var relativePath = entry.FullName.Replace('/', Path.DirectorySeparatorChar);
                if (relativePath.Contains("..")) continue;

                var destPath = Path.Combine(userFolder, relativePath);
                var destDir = Path.GetDirectoryName(destPath);
                if (destDir != null && !Directory.Exists(destDir))
                    Directory.CreateDirectory(destDir);

                entry.ExtractToFile(destPath, overwrite: true);

                if (entry.Name.EndsWith(".skill.md", StringComparison.OrdinalIgnoreCase)
                    || entry.Name.Equals("SKILL.md", StringComparison.OrdinalIgnoreCase))
                    importedCount++;
            }

            if (importedCount > 0)
            {
                LogService.Info($"스킬 가져오기 완료: {importedCount}개 스킬 ({zipPath})");
                // 스킬 목록 리로드
                LoadSkills();
            }

            return importedCount;
        }
        catch (Exception ex)
        {
            LogService.Warn($"스킬 가져오기 실패: {ex.Message}");
            return 0;
        }
    }

    // ─── 도구 이름 매핑 (외부 스킬 호환) ──────────────────────────────────────

    /// <summary>
    /// 외부 스킬(agentskills.io 등)의 도구 이름을 AX Copilot 내부 도구 이름으로 매핑합니다.
    /// 스킬 시스템 프롬프트에서 외부 도구명을 내부 도구명으로 치환하여 호환성을 확보합니다.
    /// </summary>
    private static readonly Dictionary<string, string> ToolNameMap = new(StringComparer.OrdinalIgnoreCase)
    {
        // Claude Code / Cursor 표준
        ["Bash"] = "process",
        ["bash"] = "process",
        ["Read"] = "file_read",
        ["Write"] = "file_write",
        ["Edit"] = "file_edit",
        ["Glob"] = "glob",
        ["Grep"] = "grep_tool",
        ["WebSearch"] = "http_tool",
        ["WebFetch"] = "http_tool",
        // agentskills.io 표준
        ["execute_command"] = "process",
        ["read_file"] = "file_read",
        ["write_file"] = "file_write",
        ["edit_file"] = "file_edit",
        ["search_files"] = "glob",
        ["search_content"] = "grep_tool",
        ["list_files"] = "folder_map",
        // 기타 일반적 도구명
        ["shell"] = "process",
        ["terminal"] = "process",
        ["cat"] = "file_read",
        ["find"] = "glob",
        ["rg"] = "grep_tool",
        ["git"] = "git_tool",
    };

    /// <summary>스킬 본문의 외부 도구 이름을 내부 도구 이름으로 매핑합니다.</summary>
    public static string MapToolNames(string skillBody)
    {
        if (string.IsNullOrEmpty(skillBody)) return skillBody;

        foreach (var kv in ToolNameMap)
        {
            // 코드 블록 내 도구 참조: `Bash`, `Read` 등을 `process`, `file_read`로 변환
            skillBody = skillBody.Replace($"`{kv.Key}`", $"`{kv.Value}`");
            // 괄호 내 참조: (Bash), (Read) 패턴
            skillBody = skillBody.Replace($"({kv.Key})", $"({kv.Value})");
        }

        return skillBody;
    }
}
