using System.IO;
using System.Text.Json;
using System.Text.Json.Nodes;
using AxCopilot.SDK;
using AxCopilot.Services;
using AxCopilot.Themes;

namespace AxCopilot.Handlers;

/// <summary>
/// Chrome / Edge 브라우저 북마크를 검색합니다.
/// 프리픽스 없음 — 일반 퍼지 검색에 통합됩니다.
/// 지원 브라우저: Google Chrome, Microsoft Edge
/// </summary>
public class BookmarkHandler : IActionHandler
{
    public string? Prefix => null;  // 퍼지 검색에 통합

    public PluginMetadata Metadata => new(
        "Bookmarks",
        "Chrome / Edge 북마크 검색",
        "1.0",
        "AX");

    // 캐시 (앱 세션 중 북마크가 자주 바뀌지 않으므로 한 번 로드 후 재사용)
    private List<BookmarkEntry>? _cache;
    private DateTime _cacheTime = DateTime.MinValue;
    private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(5);

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(query))
            return Task.FromResult<IEnumerable<LauncherItem>>(Array.Empty<LauncherItem>());

        RefreshCacheIfNeeded();

        if (_cache == null || _cache.Count == 0)
            return Task.FromResult<IEnumerable<LauncherItem>>(Array.Empty<LauncherItem>());

        var q = query.Trim().ToLowerInvariant();
        var results = _cache
            .Where(b => b.Name.ToLowerInvariant().Contains(q)
                     || (b.Url?.ToLowerInvariant().Contains(q) ?? false))
            .Take(8)
            .Select(b => new LauncherItem(
                b.Name,
                b.Url ?? "",
                null,
                b.Url,
                Symbol: Symbols.Globe,
                Group: "📑 북마크"))
            .ToList();

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is string url && !string.IsNullOrWhiteSpace(url))
        {
            try
            {
                System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url)
                    { UseShellExecute = true });
            }
            catch (Exception ex)
            {
                LogService.Warn($"북마크 열기 실패: {ex.Message}");
            }
        }
        return Task.CompletedTask;
    }

    // ─── 캐시 ────────────────────────────────────────────────────────────────

    private void RefreshCacheIfNeeded()
    {
        if (_cache != null && DateTime.Now - _cacheTime < CacheTtl) return;

        var bookmarks = new List<BookmarkEntry>();

        foreach (var file in GetBookmarkFiles())
        {
            try
            {
                var json = File.ReadAllText(file);
                ParseChromeBookmarks(json, bookmarks);
            }
            catch (Exception ex)
            {
                LogService.Warn($"북마크 파일 읽기 실패: {file} — {ex.Message}");
            }
        }

        _cache = bookmarks;
        _cacheTime = DateTime.Now;
        LogService.Info($"북마크 로드 완료: {bookmarks.Count}개");
    }

    // ─── 북마크 파일 경로 ─────────────────────────────────────────────────────

    private static IEnumerable<string> GetBookmarkFiles()
    {
        var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

        // Chrome (안정, 베타, 개발, 카나리아)
        var chromePaths = new[]
        {
            Path.Combine(localAppData, "Google", "Chrome", "User Data"),
            Path.Combine(localAppData, "Google", "Chrome Beta", "User Data"),
            Path.Combine(localAppData, "Google", "Chrome Dev", "User Data"),
            Path.Combine(localAppData, "Google", "Chrome SxS", "User Data"),
        };

        // Edge
        var edgePaths = new[]
        {
            Path.Combine(localAppData, "Microsoft", "Edge", "User Data"),
            Path.Combine(localAppData, "Microsoft", "Edge Beta", "User Data"),
            Path.Combine(localAppData, "Microsoft", "Edge Dev", "User Data"),
            Path.Combine(localAppData, "Microsoft", "Edge Canary", "User Data"),
        };

        foreach (var profileRoot in chromePaths.Concat(edgePaths))
        {
            if (!Directory.Exists(profileRoot)) continue;

            // Default 프로파일
            var defaultBookmark = Path.Combine(profileRoot, "Default", "Bookmarks");
            if (File.Exists(defaultBookmark)) yield return defaultBookmark;

            // Profile 1, 2, ... 프로파일
            foreach (var dir in Directory.GetDirectories(profileRoot, "Profile *"))
            {
                var f = Path.Combine(dir, "Bookmarks");
                if (File.Exists(f)) yield return f;
            }
        }
    }

    // ─── Chrome/Edge JSON 파싱 ───────────────────────────────────────────────

    private static void ParseChromeBookmarks(string json, List<BookmarkEntry> result)
    {
        var doc = JsonNode.Parse(json);
        var roots = doc?["roots"];
        if (roots == null) return;

        foreach (var rootKey in new[] { "bookmark_bar", "other", "synced" })
        {
            var node = roots[rootKey];
            if (node != null) WalkNode(node, result);
        }
    }

    private static void WalkNode(JsonNode node, List<BookmarkEntry> result)
    {
        var type = node["type"]?.GetValue<string>();

        if (type == "url")
        {
            var name = node["name"]?.GetValue<string>() ?? "";
            var url  = node["url"]?.GetValue<string>() ?? "";
            if (!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(url))
                result.Add(new BookmarkEntry(name, url));
            return;
        }

        if (type == "folder")
        {
            var children = node["children"]?.AsArray();
            if (children == null) return;
            foreach (var child in children)
            {
                if (child != null) WalkNode(child, result);
            }
        }
    }

    private record BookmarkEntry(string Name, string? Url);
}
