using System.Diagnostics;
using System.IO;
using AxCopilot.Models;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// @ prefix 핸들러: URL 열기 Alias
/// </summary>
public class UrlAliasHandler : IActionHandler
{
    private readonly SettingsService _settings;
    public string? Prefix => "@";
    public PluginMetadata Metadata => new("url-alias", "URL 별칭", "1.0", "AX");

    public UrlAliasHandler(SettingsService settings) => _settings = settings;

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var aliases = _settings.Settings.Aliases
            .Where(a => a.Type == "url" &&
                        (string.IsNullOrEmpty(query) ||
                         a.Key.Contains(query, StringComparison.OrdinalIgnoreCase)))
            .Select(a =>
            {
                // favicon 캐시 경로 조회 (없으면 백그라운드 다운로드 시작)
                var faviconPath = GetFaviconPath(a.Target);
                return new LauncherItem(
                    a.Key,
                    a.Description ?? a.Target,
                    faviconPath,
                    a,
                    a.Target,
                    Symbols.Globe);
            });

        return Task.FromResult<IEnumerable<LauncherItem>>(aliases);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is AliasEntry alias)
        {
            var url = Environment.ExpandEnvironmentVariables(alias.Target);
            if (!IsAllowedUrl(url))
            {
                LogService.Warn($"허용되지 않는 URL 스킴: {url}");
                return Task.CompletedTask;
            }
            Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
        }
        return Task.CompletedTask;
    }

    // http, https, ftp, ms-settings, mailto, file 스킴만 허용 (javascript: 등 차단)
    private static readonly HashSet<string> AllowedSchemes =
        new(StringComparer.OrdinalIgnoreCase) { "http", "https", "ftp", "ftps", "ms-settings", "mailto", "file" };

    private static bool IsAllowedUrl(string url)
    {
        if (!Uri.TryCreate(url, UriKind.Absolute, out var uri)) return false;
        return AllowedSchemes.Contains(uri.Scheme);
    }

    /// <summary>favicon 캐시 파일 경로를 반환합니다. 캐시에 없으면 백그라운드 다운로드를 시작합니다.</summary>
    private static string? GetFaviconPath(string url)
    {
        try
        {
            if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
                url = "https://" + url;
            var domain = new Uri(url).Host.ToLowerInvariant();
            var cachePath = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                "AxCopilot", "favicons", $"{domain}.png");

            if (File.Exists(cachePath))
                return cachePath;

            // 백그라운드 다운로드 시작 (다음 표시 시 캐시됨)
            FaviconService.GetFavicon(url);
            return null;
        }
        catch (Exception) { return null; }
    }
}

/// <summary>
/// cd prefix 핸들러: 폴더 열기 Alias
/// </summary>
public class FolderAliasHandler : IActionHandler
{
    private readonly SettingsService _settings;
    public string? Prefix => "cd";
    public PluginMetadata Metadata => new("folder-alias", "폴더 별칭", "1.0", "AX");

    public FolderAliasHandler(SettingsService settings) => _settings = settings;

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var aliases = _settings.Settings.Aliases
            .Where(a => a.Type == "folder" &&
                        (string.IsNullOrEmpty(query) ||
                         a.Key.Contains(query, StringComparison.OrdinalIgnoreCase)))
            .Select(a => new LauncherItem(
                a.Key,
                Environment.ExpandEnvironmentVariables(a.Target),
                null,
                a,
                Symbol: Symbols.Folder));

        return Task.FromResult<IEnumerable<LauncherItem>>(aliases);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is AliasEntry alias)
        {
            var path = Environment.ExpandEnvironmentVariables(alias.Target);
            Process.Start(new ProcessStartInfo("explorer.exe", path) { UseShellExecute = true });
        }
        return Task.CompletedTask;
    }
}

/// <summary>
/// > prefix 핸들러: 터미널 명령 실행
/// </summary>
public class BatchHandler : IActionHandler
{
    private readonly SettingsService _settings;
    public string? Prefix => ">";
    public PluginMetadata Metadata => new("batch", "명령 실행", "1.0", "AX");

    public BatchHandler(SettingsService settings) => _settings = settings;

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        // 등록된 배치 Alias 표시 + 직접 입력 실행 옵션
        var items = new List<LauncherItem>();

        var batchAliases = _settings.Settings.Aliases
            .Where(a => a.Type == "batch" &&
                        (string.IsNullOrEmpty(query) ||
                         a.Key.Contains(query, StringComparison.OrdinalIgnoreCase)))
            .Select(a => new LauncherItem(a.Key, a.Target, null, a, Symbol: Symbols.Terminal));

        items.AddRange(batchAliases);

        // 직접 명령어 입력 허용 (큰따옴표 이스케이프로 인젝션 방지)
        if (!string.IsNullOrEmpty(query))
        {
            var safeQuery = query.Replace("\"", "\\\"");
            items.Insert(0, new LauncherItem(
                $"실행: {query}",
                "PowerShell에서 직접 실행",
                null,
                new AliasEntry { Type = "batch", Target = $"powershell -NoProfile -Command \"{safeQuery}\"", ShowWindow = true },
                Symbol: Symbols.Terminal
            ));
        }

        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is AliasEntry alias)
        {
            var target = Environment.ExpandEnvironmentVariables(alias.Target);
            var parts = target.Split(' ', 2);
            var psi = new ProcessStartInfo(parts[0])
            {
                Arguments = parts.Length > 1 ? parts[1] : "",
                UseShellExecute = alias.ShowWindow,
                CreateNoWindow = !alias.ShowWindow,
                WindowStyle = alias.ShowWindow ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden
            };
            Process.Start(psi);
        }
        return Task.CompletedTask;
    }
}
