using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

/// <summary>
/// L23-2: 연차·휴가 관리. "leave" 프리픽스로 사용합니다.
///
/// 예: leave                       → 잔여 연차 현황
///     leave set 15                → 연간 연차 일수 설정
///     leave use 2026-04-10        → 1일 연차 기록
///     leave use 2026-04-10 0.5    → 반차 기록
///     leave use 2026-04-10 0.5 반차메모 → 메모 포함
///     leave del 2026-04-10        → 기록 삭제
///     leave remaining             → 잔여 연차
///     leave list                  → 올해 사용 이력
///     leave clear                 → 올해 기록 초기화
/// Enter → 저장 실행 또는 복사
/// 저장: %APPDATA%\AxCopilot\leave.json
/// </summary>
public class LeaveHandler : IActionHandler
{
    public string? Prefix => "leave";

    public PluginMetadata Metadata => new(
        "연차 관리",
        "연차·휴가 기록 — 설정 · 사용 · 잔여 조회 · 이력",
        "1.0",
        "AX");

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

    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        WriteIndented = true,
        Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
    };

    private sealed class LeaveData
    {
        [JsonPropertyName("annualDays")] public double AnnualDays { get; set; } = 15;
        [JsonPropertyName("records")]    public List<LeaveRecord> Records { get; set; } = [];
    }

    private sealed class LeaveRecord
    {
        [JsonPropertyName("date")] public string Date { get; set; } = "";
        [JsonPropertyName("days")] public double Days { get; set; } = 1;
        [JsonPropertyName("note")] public string Note { get; set; } = "";
    }

    // ── GetItemsAsync ─────────────────────────────────────────────────────────

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var items = new List<LauncherItem>();
        var data  = LoadData();
        var year  = DateTime.Today.Year;
        var used  = data.Records.Where(r => r.Date.StartsWith(year.ToString())).Sum(r => r.Days);
        var left  = data.AnnualDays - used;

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem(
                $"연차 현황 — 연간 {data.AnnualDays}일  사용 {used}일  잔여 {left}일",
                "leave set N · leave use 날짜 [일수] [메모] · leave list · leave remaining",
                null, null, Symbol: "\uE716"));
            items.Add(new LauncherItem("leave set <일수>",    "연간 연차 총일수 설정",       null, null, Symbol: "\uE716"));
            items.Add(new LauncherItem("leave use <날짜>",    "연차 사용 기록 (기본 1일)",   null, null, Symbol: "\uE716"));
            items.Add(new LauncherItem("leave list",          "올해 사용 이력 전체 조회",    null, null, Symbol: "\uE716"));
            items.Add(new LauncherItem("leave remaining",     "잔여 연차 확인",              null, null, Symbol: "\uE716"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        var sub   = parts[0].ToLowerInvariant();

        // set N
        if (sub == "set")
        {
            if (parts.Length >= 2 && double.TryParse(parts[1], out var n) && n > 0)
            {
                items.Add(new LauncherItem(
                    $"연간 연차를 {n}일로 설정",
                    $"현재: {data.AnnualDays}일  →  {n}일  ·  Enter: 저장",
                    null, ("set", n.ToString(System.Globalization.CultureInfo.InvariantCulture)),
                    Symbol: "\uE716"));
            }
            else
            {
                items.Add(new LauncherItem("일수를 입력하세요", "예: leave set 15", null, null, Symbol: "\uE783"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // use 날짜 [일수] [메모]
        if (sub == "use")
        {
            if (parts.Length < 2)
            {
                items.Add(new LauncherItem("날짜를 입력하세요", "예: leave use 2026-04-10", null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            var dateStr = parts[1];
            if (!DateOnly.TryParseExact(dateStr, new[] { "yyyy-MM-dd", "yyyy/MM/dd" },
                System.Globalization.CultureInfo.InvariantCulture,
                System.Globalization.DateTimeStyles.None, out _))
            {
                items.Add(new LauncherItem("날짜 형식 오류", "yyyy-MM-dd 형식으로 입력하세요", null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            var useDays = 1.0;
            var note    = "";
            if (parts.Length >= 3 && double.TryParse(parts[2],
                System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out var pd))
            {
                useDays = pd;
                if (parts.Length >= 4)
                    note = string.Join(" ", parts[3..]);
            }
            else if (parts.Length >= 3)
            {
                note = string.Join(" ", parts[2..]);
            }
            var newLeft = left - useDays;
            var label   = note.Length > 0
                ? $"{dateStr} 연차 {useDays}일 기록 [{note}]"
                : $"{dateStr} 연차 {useDays}일 기록";
            items.Add(new LauncherItem(
                label,
                $"잔여: {newLeft}일  ·  Enter: 저장",
                null, ("use", $"{dateStr}|{useDays.ToString(System.Globalization.CultureInfo.InvariantCulture)}|{note}"),
                Symbol: "\uE716"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // del 날짜
        if (sub == "del")
        {
            if (parts.Length < 2)
            {
                items.Add(new LauncherItem("날짜를 입력하세요", "예: leave del 2026-04-10", null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            var delDate = parts[1];
            var target  = data.Records.FirstOrDefault(r => r.Date == delDate);
            if (target == null)
            {
                items.Add(new LauncherItem($"{delDate} 기록 없음", "해당 날짜의 연차 기록이 없습니다", null, null, Symbol: "\uE783"));
            }
            else
            {
                items.Add(new LauncherItem(
                    $"{delDate} 기록 삭제  ({target.Days}일{(target.Note.Length > 0 ? " / " + target.Note : "")})",
                    "Enter: 삭제",
                    null, ("del", delDate), Symbol: "\uE716"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // remaining
        if (sub == "remaining")
        {
            items.Add(new LauncherItem(
                $"잔여 연차: {left}일  (연간 {data.AnnualDays}일 - 사용 {used}일)",
                "Enter: 클립보드 복사",
                null, ("copy", $"잔여 연차: {left}일"), Symbol: "\uE716"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // list
        if (sub == "list")
        {
            var yearRecords = data.Records
                .Where(r => r.Date.StartsWith(year.ToString()))
                .OrderBy(r => r.Date)
                .ToList();
            items.Add(new LauncherItem(
                $"{year}년 연차 사용 이력 — {yearRecords.Count}건  총 {used}일",
                $"잔여: {left}일", null, null, Symbol: "\uE716"));
            if (yearRecords.Count == 0)
            {
                items.Add(new LauncherItem("사용 이력 없음", "", null, null, Symbol: "\uE716"));
            }
            else
            {
                foreach (var r in yearRecords)
                {
                    var noteStr = r.Note.Length > 0 ? $"  [{r.Note}]" : "";
                    items.Add(new LauncherItem(
                        $"{r.Date}  {r.Days}일{noteStr}",
                        "Enter: 삭제",
                        null, ("del", r.Date), Symbol: "\uE716"));
                }
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // clear
        if (sub == "clear")
        {
            var cnt = data.Records.Count(r => r.Date.StartsWith(year.ToString()));
            items.Add(new LauncherItem(
                $"{year}년 연차 기록 초기화  ({cnt}건)",
                "Enter: 확인",
                null, ("clear", year.ToString()), Symbol: "\uE716"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 기본 — 안내
        items.Add(new LauncherItem($"'{q}' — 알 수 없는 명령",
            "leave set · use · del · remaining · list · clear",
            null, null, Symbol: "\uE783"));
        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    // ── ExecuteAsync ──────────────────────────────────────────────────────────

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        var data = LoadData();

        switch (item.Data)
        {
            case ("set", string nStr) when double.TryParse(nStr,
                System.Globalization.NumberStyles.Any,
                System.Globalization.CultureInfo.InvariantCulture, out var n):
                data.AnnualDays = n;
                SaveData(data);
                NotificationService.Notify("연차 관리", $"연간 연차를 {n}일로 설정했습니다.");
                break;

            case ("use", string payload):
            {
                var parts = payload.Split('|');
                if (parts.Length >= 2 &&
                    double.TryParse(parts[1],
                        System.Globalization.NumberStyles.Any,
                        System.Globalization.CultureInfo.InvariantCulture, out var days))
                {
                    var dateStr = parts[0];
                    var note    = parts.Length >= 3 ? parts[2] : "";
                    // 중복 날짜 처리 — 누적
                    var existing = data.Records.FirstOrDefault(r => r.Date == dateStr);
                    if (existing != null)
                    {
                        existing.Days += days;
                        if (note.Length > 0) existing.Note = note;
                    }
                    else
                    {
                        data.Records.Add(new LeaveRecord { Date = dateStr, Days = days, Note = note });
                    }
                    data.Records.Sort((a, b) => string.Compare(a.Date, b.Date, StringComparison.Ordinal));
                    SaveData(data);
                    NotificationService.Notify("연차 관리", $"{dateStr} 연차 {days}일 기록됐습니다.");
                }
                break;
            }

            case ("del", string delDate):
            {
                var removed = data.Records.RemoveAll(r => r.Date == delDate);
                if (removed > 0)
                {
                    SaveData(data);
                    NotificationService.Notify("연차 관리", $"{delDate} 기록이 삭제됐습니다.");
                }
                break;
            }

            case ("clear", string yearStr) when int.TryParse(yearStr, out var y):
            {
                var cnt = data.Records.RemoveAll(r => r.Date.StartsWith(y.ToString()));
                SaveData(data);
                NotificationService.Notify("연차 관리", $"{y}년 연차 기록 {cnt}건 초기화됐습니다.");
                break;
            }

            case ("copy", string text):
                try
                {
                    Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
                    NotificationService.Notify("연차 관리", "클립보드에 복사했습니다.");
                }
                catch { }
                break;
        }
        return Task.CompletedTask;
    }

    // ── 저장/불러오기 ─────────────────────────────────────────────────────────

    private static LeaveData LoadData()
    {
        try
        {
            if (!System.IO.File.Exists(DataPath)) return new LeaveData();
            var json = System.IO.File.ReadAllText(DataPath, System.Text.Encoding.UTF8);
            return JsonSerializer.Deserialize<LeaveData>(json, JsonOpts) ?? new LeaveData();
        }
        catch { return new LeaveData(); }
    }

    private static void SaveData(LeaveData data)
    {
        try
        {
            System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(DataPath)!);
            System.IO.File.WriteAllText(DataPath,
                JsonSerializer.Serialize(data, JsonOpts),
                System.Text.Encoding.UTF8);
        }
        catch { }
    }
}
