using System.IO;
using System.IO.Compression;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace AxCopilot.Services.Agent;

/// <summary>플러그인 매니페스트 (plugin.json 파일).</summary>
public record PluginManifest
{
    [JsonPropertyName("id")]
    public string Id { get; init; } = "";

    [JsonPropertyName("name")]
    public string Name { get; init; } = "";

    [JsonPropertyName("version")]
    public string Version { get; init; } = "1.0.0";

    [JsonPropertyName("description")]
    public string Description { get; init; } = "";

    [JsonPropertyName("author")]
    public string Author { get; init; } = "";

    [JsonPropertyName("type")]
    public string Type { get; init; } = "skill"; // skill | tool | theme

    [JsonPropertyName("entryPoint")]
    public string EntryPoint { get; init; } = ""; // skill file, DLL name, etc.

    [JsonPropertyName("minVersion")]
    public string MinAppVersion { get; init; } = "1.0.0";

    /// <summary>설치 후 로드할 추가 파일 목록.</summary>
    [JsonPropertyName("files")]
    public List<string> Files { get; init; } = new();
}

/// <summary>설치된 플러그인 항목.</summary>
public record InstalledPlugin
{
    public PluginManifest Manifest { get; init; } = new();
    public string InstallPath { get; init; } = "";
    public DateTime InstalledAt { get; init; }
    public bool IsEnabled { get; set; } = true;
}

/// <summary>플러그인 설치 결과.</summary>
public record PluginInstallResult
{
    public bool Success { get; init; }
    public string Message { get; init; } = "";
    public PluginManifest? Manifest { get; init; }
}

/// <summary>
/// Phase 18-C: 로컬 zip 파일 기반 플러그인 설치 서비스.
/// URL 다운로드 없음 — 사내 환경 보안 정책 준수.
/// </summary>
public class PluginInstallService
{
    private readonly string _pluginsDir;
    private readonly string _registryFile;
    private List<InstalledPlugin> _installed = new();

    public PluginInstallService()
    {
        _pluginsDir = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "AxCopilot", "plugins");
        _registryFile = Path.Combine(_pluginsDir, "registry.json");
        Directory.CreateDirectory(_pluginsDir);
        LoadRegistry();
    }

    /// <summary>설치된 플러그인 목록.</summary>
    public IReadOnlyList<InstalledPlugin> Installed => _installed.AsReadOnly();

    /// <summary>
    /// 로컬 zip 파일에서 플러그인을 설치합니다.
    /// zip 내부에 plugin.json 매니페스트가 있어야 합니다.
    /// </summary>
    public async Task<PluginInstallResult> InstallFromZipAsync(string zipFilePath)
    {
        if (!File.Exists(zipFilePath))
            return new PluginInstallResult { Success = false, Message = $"파일을 찾을 수 없습니다: {zipFilePath}" };

        if (Path.GetExtension(zipFilePath).ToLowerInvariant() != ".zip")
            return new PluginInstallResult { Success = false, Message = "zip 파일만 설치할 수 있습니다." };

        try
        {
            // zip에서 manifest 읽기
            PluginManifest? manifest = null;
            using (var zip = ZipFile.OpenRead(zipFilePath))
            {
                var manifestEntry = zip.GetEntry("plugin.json")
                    ?? zip.Entries.FirstOrDefault(e => e.Name == "plugin.json");

                if (manifestEntry == null)
                    return new PluginInstallResult { Success = false, Message = "plugin.json 매니페스트가 없습니다." };

                using var stream = manifestEntry.Open();
                manifest = await JsonSerializer.DeserializeAsync<PluginManifest>(stream);
            }

            if (manifest == null || string.IsNullOrWhiteSpace(manifest.Id))
                return new PluginInstallResult { Success = false, Message = "유효하지 않은 매니페스트입니다." };

            // ID 유효성 검사 (alphanumeric + _ + -)
            if (!System.Text.RegularExpressions.Regex.IsMatch(manifest.Id, @"^[\w\-]+$"))
                return new PluginInstallResult { Success = false, Message = $"유효하지 않은 플러그인 ID: {manifest.Id}" };

            // 이미 설치된 플러그인 업데이트 여부 확인
            var existing = _installed.FirstOrDefault(p => p.Manifest.Id == manifest.Id);
            var installPath = Path.Combine(_pluginsDir, manifest.Id);

            // 기존 설치 제거
            if (Directory.Exists(installPath))
                Directory.Delete(installPath, recursive: true);
            Directory.CreateDirectory(installPath);

            // zip 압축 해제
            ZipFile.ExtractToDirectory(zipFilePath, installPath, overwriteFiles: true);

            var plugin = new InstalledPlugin
            {
                Manifest = manifest,
                InstallPath = installPath,
                InstalledAt = DateTime.UtcNow,
                IsEnabled = true
            };

            if (existing != null)
                _installed.Remove(existing);
            _installed.Add(plugin);

            await SaveRegistryAsync();

            var action = existing != null ? "업데이트" : "설치";
            return new PluginInstallResult
            {
                Success = true,
                Message = $"'{manifest.Name}' v{manifest.Version} {action} 완료.",
                Manifest = manifest
            };
        }
        catch (InvalidDataException)
        {
            return new PluginInstallResult { Success = false, Message = "손상된 zip 파일입니다." };
        }
        catch (Exception ex)
        {
            return new PluginInstallResult { Success = false, Message = $"설치 오류: {ex.Message}" };
        }
    }

    /// <summary>플러그인을 제거합니다.</summary>
    public async Task<bool> UninstallAsync(string pluginId)
    {
        var plugin = _installed.FirstOrDefault(p => p.Manifest.Id == pluginId);
        if (plugin == null) return false;

        try
        {
            if (Directory.Exists(plugin.InstallPath))
                Directory.Delete(plugin.InstallPath, recursive: true);
            _installed.Remove(plugin);
            await SaveRegistryAsync();
            return true;
        }
        catch (Exception) { return false; }
    }

    /// <summary>플러그인 활성화/비활성화 토글.</summary>
    public async Task SetEnabledAsync(string pluginId, bool enabled)
    {
        var plugin = _installed.FirstOrDefault(p => p.Manifest.Id == pluginId);
        if (plugin == null) return;
        plugin.IsEnabled = enabled;
        await SaveRegistryAsync();
    }

    /// <summary>스킬 타입 플러그인의 .skill.md 파일들을 반환 (SkillService 로드용).</summary>
    public IReadOnlyList<string> GetSkillFilePaths()
    {
        return _installed
            .Where(p => p.IsEnabled && p.Manifest.Type == "skill")
            .Select(p => Path.Combine(p.InstallPath, p.Manifest.EntryPoint))
            .Where(File.Exists)
            .ToList();
    }

    private void LoadRegistry()
    {
        if (!File.Exists(_registryFile)) return;
        try
        {
            var json = File.ReadAllText(_registryFile, System.Text.Encoding.UTF8);
            _installed = JsonSerializer.Deserialize<List<InstalledPlugin>>(json) ?? new List<InstalledPlugin>();
        }
        catch (Exception) { _installed = new List<InstalledPlugin>(); }
    }

    private async Task SaveRegistryAsync()
    {
        var json = JsonSerializer.Serialize(_installed, new JsonSerializerOptions { WriteIndented = true });
        await File.WriteAllTextAsync(_registryFile, json, System.Text.Encoding.UTF8);
    }
}
