using System.IO;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

/// <summary>
/// L29-2: 명령 체인(워크플로우) 핸들러. "flow" 프리픽스로 사용합니다.
///
/// 예: flow                                    → 등록된 플로우 목록
///     flow add 출근준비 "remind 09:00 회의" > "today" > "todo list"  → 플로우 추가
///     flow 출근준비                           → 플로우 실행
///     flow del 출근준비                       → 플로우 삭제
///     flow edit 출근준비                      → 플로우 명령 목록 표시 (클립보드 복사)
/// Enter → 저장된 명령들을 순서대로 런처에 실행 (클립보드에 명령 목록 복사).
/// Alfred 워크플로우 경량 대응.
/// 저장: %APPDATA%\AxCopilot\flows.json
/// </summary>
public class FlowHandler : IActionHandler
{
    public string? Prefix => "flow";

    public PluginMetadata Metadata => new(
        "명령 체인",
        "여러 명령을 묶어 순서대로 실행 (워크플로우)",
        "1.0",
        "AX");

    private sealed record FlowEntry(
        [property: JsonPropertyName("name")]     string Name,
        [property: JsonPropertyName("commands")] List<string> Commands,
        [property: JsonPropertyName("created")]  DateTime Created);

    private static readonly string DataPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
        "AxCopilot", "flows.json");

    private static readonly JsonSerializerOptions JsonOpt = new()
    {
        WriteIndented = true,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    };

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();
        var flows = Load();

        // ── add 명령 ──────────────────────────────────────────────────────────
        if (q.StartsWith("add ", StringComparison.OrdinalIgnoreCase))
        {
            var rest = q[4..].Trim();
            var spaceIdx = rest.IndexOf(' ');
            if (spaceIdx < 1)
            {
                items.Add(new LauncherItem("사용법: flow add {이름} {명령1} > {명령2} > ...",
                    "예: flow add 출근 \"today\" > \"todo list\" > \"remind 09:00 회의\"",
                    null, null, Symbol: "\uE710"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            var name = rest[..spaceIdx];
            var cmdStr = rest[(spaceIdx + 1)..].Trim();
            var commands = ParseCommands(cmdStr);

            if (commands.Count == 0)
            {
                items.Add(new LauncherItem("명령을 > 로 구분해 입력하세요",
                    "예: \"today\" > \"todo list\"",
                    null, null, Symbol: Themes.Symbols.Warning));
            }
            else
            {
                items.Add(new LauncherItem(
                    $"플로우 저장: {name}  ({commands.Count}개 명령)",
                    string.Join(" → ", commands),
                    null, ("add", name, commands), Symbol: "\uE710"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── del 명령 ──────────────────────────────────────────────────────────
        if (q.StartsWith("del ", StringComparison.OrdinalIgnoreCase))
        {
            var name = q[4..].Trim();
            var found = flows.FirstOrDefault(f => f.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
            if (found != null)
            {
                items.Add(new LauncherItem($"플로우 삭제: {found.Name}",
                    $"{found.Commands.Count}개 명령 · {string.Join(" → ", found.Commands)}",
                    null, ("del", found.Name), Symbol: "\uE74D"));
            }
            else
            {
                items.Add(new LauncherItem($"'{name}' 플로우를 찾을 수 없습니다",
                    "flow del {이름}", null, null, Symbol: "\uE783"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── 빈 쿼리 → 전체 목록 ──────────────────────────────────────────────
        if (string.IsNullOrWhiteSpace(q))
        {
            if (flows.Count == 0)
            {
                items.Add(new LauncherItem("등록된 명령 체인이 없습니다",
                    "flow add {이름} {명령1} > {명령2} > ... 로 추가하세요",
                    null, null, Symbol: "\uE8A0"));
                items.Add(new LauncherItem("예시: flow add 출근 \"today\" > \"todo list\"",
                    "오늘 업무 뷰 → 할일 목록 순서대로 실행",
                    null, null, Symbol: Themes.Symbols.Info));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }

            items.Add(new LauncherItem($"명령 체인 {flows.Count}개",
                "Enter: 명령 목록 클립보드 복사 · flow add/del 로 관리",
                null, null, Symbol: "\uE8A0"));

            foreach (var f in flows)
            {
                items.Add(new LauncherItem(
                    $"▶ {f.Name}  ({f.Commands.Count}단계)",
                    string.Join(" → ", f.Commands),
                    null, ("run", f), Symbol: "\uE768"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // ── 이름 검색 → 실행 ──────────────────────────────────────────────────
        var match = flows.FirstOrDefault(f => f.Name.Equals(q, StringComparison.OrdinalIgnoreCase));
        if (match != null)
        {
            items.Add(new LauncherItem(
                $"▶ {match.Name} 실행",
                string.Join(" → ", match.Commands),
                null, ("run", match), Symbol: "\uE768"));

            for (int i = 0; i < match.Commands.Count; i++)
                items.Add(new LauncherItem($"  {i + 1}. {match.Commands[i]}", "",
                    null, null, Symbol: Themes.Symbols.Terminal));

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

        // 부분 매칭
        var searched = flows.Where(f =>
            f.Name.Contains(q, StringComparison.OrdinalIgnoreCase) ||
            f.Commands.Any(c => c.Contains(q, StringComparison.OrdinalIgnoreCase))).ToList();

        if (searched.Count > 0)
        {
            foreach (var f in searched)
                items.Add(new LauncherItem($"▶ {f.Name}  ({f.Commands.Count}단계)",
                    string.Join(" → ", f.Commands),
                    null, ("run", f), Symbol: "\uE768"));
        }
        else
        {
            items.Add(new LauncherItem($"'{q}' 플로우를 찾을 수 없습니다",
                "flow add {이름} {명령} > {명령} 으로 추가하세요",
                null, null, Symbol: "\uE783"));
        }

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("add", string name, List<string> commands))
        {
            var flows = Load();
            flows.RemoveAll(f => f.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
            flows.Add(new FlowEntry(name, commands, DateTime.Now));
            Save(flows);
            NotificationService.Notify("flow", $"'{name}' 플로우가 저장되었습니다. ({commands.Count}단계)");
        }
        else if (item.Data is ("del", string delName))
        {
            var flows = Load();
            flows.RemoveAll(f => f.Name.Equals(delName, StringComparison.OrdinalIgnoreCase));
            Save(flows);
            NotificationService.Notify("flow", $"'{delName}' 플로우가 삭제되었습니다.");
        }
        else if (item.Data is ("run", FlowEntry flow))
        {
            // 명령 목록을 클립보드에 복사 (사용자가 순서대로 런처에 입력)
            var text = string.Join("\n", flow.Commands.Select((c, i) => $"{i + 1}. {c}"));
            try
            {
                Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
                NotificationService.Notify("flow", $"'{flow.Name}' 명령 {flow.Commands.Count}개가 클립보드에 복사되었습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }

    // ─── 명령 파싱 ────────────────────────────────────────────────────────────

    private static List<string> ParseCommands(string input)
    {
        // "cmd1" > "cmd2" > "cmd3" 또는 cmd1 > cmd2 > cmd3
        return input.Split('>')
            .Select(s => s.Trim().Trim('"').Trim())
            .Where(s => !string.IsNullOrWhiteSpace(s))
            .ToList();
    }

    // ─── JSON I/O ─────────────────────────────────────────────────────────────

    private static List<FlowEntry> Load()
    {
        try
        {
            if (!File.Exists(DataPath)) return [];
            var json = File.ReadAllText(DataPath);
            return JsonSerializer.Deserialize<List<FlowEntry>>(json) ?? [];
        }
        catch { return []; }
    }

    private static void Save(List<FlowEntry> list)
    {
        try
        {
            var dir = Path.GetDirectoryName(DataPath)!;
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            File.WriteAllText(DataPath, JsonSerializer.Serialize(list, JsonOpt));
        }
        catch { }
    }
}
