using System.IO;
using System.IO.Compression;
using System.Reflection;
using AxCopilot.Handlers;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Core;

/// <summary>
/// 외부 .dll 플러그인을 로드하고 CommandResolver에 등록합니다.
/// </summary>
public class PluginHost
{
    private readonly SettingsService _settings;
    private readonly CommandResolver _resolver;
    private readonly List<IActionHandler> _loadedPlugins = new();

    public IReadOnlyList<IActionHandler> LoadedPlugins => _loadedPlugins;

    public PluginHost(SettingsService settings, CommandResolver resolver)
    {
        _settings = settings;
        _resolver = resolver;
    }

    /// <summary>
    /// settings.json의 plugins 목록에서 .dll을 로드합니다.
    /// </summary>
    public void LoadAll()
    {
        _loadedPlugins.Clear();

        foreach (var entry in _settings.Settings.Plugins.Where(p => p.Enabled))
        {
            LoadPlugin(entry.Path);
        }

        // skills 폴더의 .skill.json 파일도 로드 (JSON 스킬)
        var skillsDir = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "AxCopilot", "skills");

        if (Directory.Exists(skillsDir))
        {
            foreach (var skillFile in Directory.EnumerateFiles(skillsDir, "*.skill.json"))
            {
                LoadJsonSkill(skillFile);
            }
        }
    }

    private void LoadPlugin(string dllPath)
    {
        if (!File.Exists(dllPath))
        {
            LogService.Warn($"플러그인 파일 없음: {dllPath}");
            return;
        }

        try
        {
            var assembly = Assembly.LoadFrom(dllPath);
            var handlerTypes = assembly.GetExportedTypes()
                .Where(t => typeof(IActionHandler).IsAssignableFrom(t) && !t.IsAbstract);

            foreach (var type in handlerTypes)
            {
                if (Activator.CreateInstance(type) is IActionHandler handler)
                {
                    _resolver.RegisterHandler(handler);
                    _loadedPlugins.Add(handler);
                    LogService.Info($"플러그인 로드: {handler.Metadata.Name} v{handler.Metadata.Version}");
                }
            }
        }
        catch (Exception ex)
        {
            LogService.Error($"플러그인 로드 실패 ({dllPath}): {ex.Message}");
            // 플러그인 오류는 앱 전체를 중단하지 않음
        }
    }

    private void LoadJsonSkill(string skillPath)
    {
        try
        {
            var skill = JsonSkillLoader.Load(skillPath);
            if (skill != null)
            {
                _resolver.RegisterHandler(skill);
                _loadedPlugins.Add(skill);
                LogService.Info($"JSON 스킬 로드: {skill.Metadata.Name}");
            }
        }
        catch (Exception ex)
        {
            LogService.Error($"JSON 스킬 로드 실패 ({skillPath}): {ex.Message}");
        }
    }

    /// <summary>
    /// 모든 플러그인을 언로드하고 재로드합니다 (개발자 모드 핫 리로드).
    /// </summary>
    public void Reload()
    {
        LogService.Info("플러그인 전체 재로드 시작");
        LoadAll();
    }

    /// <summary>
    /// 로컬 zip 파일에서 플러그인을 설치합니다.
    /// zip 내 .dll 파일을 plugins 폴더에 추출하고 settings.json에 등록합니다.
    /// </summary>
    /// <returns>설치된 핸들러 수</returns>
    public int InstallFromZip(string zipPath)
    {
        if (!File.Exists(zipPath))
        {
            LogService.Warn($"플러그인 zip 파일 없음: {zipPath}");
            return 0;
        }

        var pluginsDir = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "AxCopilot", "plugins");

        Directory.CreateDirectory(pluginsDir);

        int installed = 0;

        try
        {
            using var archive = ZipFile.OpenRead(zipPath);
            var pluginName = Path.GetFileNameWithoutExtension(zipPath);
            var targetDir = Path.Combine(pluginsDir, pluginName);
            Directory.CreateDirectory(targetDir);

            foreach (var entry in archive.Entries)
            {
                if (string.IsNullOrEmpty(entry.Name)) continue; // 폴더 엔트리 건너뛰기

                var destPath = Path.Combine(targetDir, entry.Name);

                // 경로 탐색 공격 방지
                if (!Path.GetFullPath(destPath).StartsWith(Path.GetFullPath(targetDir)))
                {
                    LogService.Warn($"플러그인 zip 경로 위험: {entry.FullName}");
                    continue;
                }

                entry.ExtractToFile(destPath, overwrite: true);
            }

            // .dll 파일 찾아서 플러그인으로 등록
            foreach (var dllFile in Directory.EnumerateFiles(targetDir, "*.dll"))
            {
                // 이미 등록된 플러그인인지 확인
                if (_settings.Settings.Plugins.Any(p => p.Path == dllFile))
                    continue;

                // settings.json에 등록
                _settings.Settings.Plugins.Add(new Models.PluginEntry
                {
                    Enabled = true,
                    Path = dllFile,
                });

                // 즉시 로드 시도
                LoadPlugin(dllFile);
                installed++;
            }

            // .skill.json 파일도 skills 폴더로 복사
            var skillsDir = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                "AxCopilot", "skills");
            Directory.CreateDirectory(skillsDir);

            foreach (var skillFile in Directory.EnumerateFiles(targetDir, "*.skill.json"))
            {
                var destSkill = Path.Combine(skillsDir, Path.GetFileName(skillFile));
                File.Copy(skillFile, destSkill, overwrite: true);
                LoadJsonSkill(destSkill);
                installed++;
            }

            if (installed > 0)
                _settings.Save();

            LogService.Info($"플러그인 설치 완료: {zipPath} → {installed}개 핸들러");
        }
        catch (Exception ex)
        {
            LogService.Error($"플러그인 zip 설치 실패: {ex.Message}");
        }

        return installed;
    }

    /// <summary>
    /// 설치된 플러그인을 제거합니다 (settings에서 삭제 + 파일 삭제).
    /// </summary>
    public bool UninstallPlugin(string dllPath)
    {
        try
        {
            var entry = _settings.Settings.Plugins.FirstOrDefault(p => p.Path == dllPath);
            if (entry != null)
            {
                _settings.Settings.Plugins.Remove(entry);
                _settings.Save();
            }

            // 플러그인 폴더 전체 삭제 (해당 dll이 있는 폴더)
            var dir = Path.GetDirectoryName(dllPath);
            if (dir != null && Directory.Exists(dir))
            {
                var pluginsRoot = Path.Combine(
                    Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                    "AxCopilot", "plugins");
                // plugins 하위 폴더인 경우만 삭제 (안전장치)
                if (Path.GetFullPath(dir).StartsWith(Path.GetFullPath(pluginsRoot)))
                {
                    Directory.Delete(dir, recursive: true);
                }
            }

            LogService.Info($"플러그인 제거 완료: {dllPath}");
            return true;
        }
        catch (Exception ex)
        {
            LogService.Error($"플러그인 제거 실패: {ex.Message}");
            return false;
        }
    }
}
