using System.Globalization;
using System.Windows;
using AxCopilot.SDK;
using AxCopilot.Services;

namespace AxCopilot.Handlers;

/// <summary>
/// L23-1: 한국 공휴일·업무일 달력. "cal" 프리픽스로 사용합니다.
///
/// 예: cal                → 이번달 달력·공휴일
///     cal next           → 다음 공휴일 D-day (5개)
///     cal workdays       → 이번달 업무일 수·잔여 업무일
///     cal today          → 오늘 공휴일 여부
///     cal 2026-05        → 특정 월 조회
///     cal 2026-04-10     → 특정 날짜 조회
/// Enter → 복사
/// </summary>
public class CalHandler : IActionHandler
{
    public string? Prefix => "cal";

    public PluginMetadata Metadata => new(
        "한국 달력",
        "공휴일·업무일 달력 — 이번달 · 다음 공휴일 · 업무일 계산",
        "1.0",
        "AX");

    // ── 공휴일 데이터 ─────────────────────────────────────────────────────────
    private static readonly Dictionary<DateOnly, string> Holidays = new()
    {
        // 2024
        { new DateOnly(2024,  1,  1), "신정" },
        { new DateOnly(2024,  2,  9), "설날연휴" },
        { new DateOnly(2024,  2, 10), "설날" },
        { new DateOnly(2024,  2, 11), "설날연휴" },
        { new DateOnly(2024,  2, 12), "대체공휴일" },
        { new DateOnly(2024,  3,  1), "삼일절" },
        { new DateOnly(2024,  4, 10), "국회의원선거" },
        { new DateOnly(2024,  5,  5), "어린이날" },
        { new DateOnly(2024,  5,  6), "대체공휴일" },
        { new DateOnly(2024,  5, 15), "부처님오신날" },
        { new DateOnly(2024,  6,  6), "현충일" },
        { new DateOnly(2024,  8, 15), "광복절" },
        { new DateOnly(2024,  9, 16), "추석연휴" },
        { new DateOnly(2024,  9, 17), "추석" },
        { new DateOnly(2024,  9, 18), "추석연휴" },
        { new DateOnly(2024, 10,  3), "개천절" },
        { new DateOnly(2024, 10,  9), "한글날" },
        { new DateOnly(2024, 12, 25), "크리스마스" },

        // 2025
        { new DateOnly(2025,  1,  1), "신정" },
        { new DateOnly(2025,  1, 28), "설날연휴" },
        { new DateOnly(2025,  1, 29), "설날" },
        { new DateOnly(2025,  1, 30), "설날연휴" },
        { new DateOnly(2025,  3,  1), "삼일절" },
        { new DateOnly(2025,  3,  3), "대체공휴일" },
        { new DateOnly(2025,  5,  5), "어린이날" },
        { new DateOnly(2025,  5,  6), "부처님오신날" },
        { new DateOnly(2025,  6,  6), "현충일" },
        { new DateOnly(2025,  8, 15), "광복절" },
        { new DateOnly(2025, 10,  3), "개천절" },
        { new DateOnly(2025, 10,  5), "추석연휴" },
        { new DateOnly(2025, 10,  6), "추석" },
        { new DateOnly(2025, 10,  7), "추석연휴" },
        { new DateOnly(2025, 10,  8), "대체공휴일" },
        { new DateOnly(2025, 10,  9), "한글날" },
        { new DateOnly(2025, 12, 25), "크리스마스" },

        // 2026
        { new DateOnly(2026,  1,  1), "신정" },
        { new DateOnly(2026,  2, 17), "설날연휴" },
        { new DateOnly(2026,  2, 18), "설날" },
        { new DateOnly(2026,  2, 19), "설날연휴" },
        { new DateOnly(2026,  3,  1), "삼일절" },
        { new DateOnly(2026,  3,  2), "대체공휴일" },
        { new DateOnly(2026,  5,  5), "어린이날" },
        { new DateOnly(2026,  5, 24), "부처님오신날" },
        { new DateOnly(2026,  5, 25), "대체공휴일" },
        { new DateOnly(2026,  6,  6), "현충일" },
        { new DateOnly(2026,  6,  8), "대체공휴일" },
        { new DateOnly(2026,  8, 15), "광복절" },
        { new DateOnly(2026,  8, 17), "대체공휴일" },
        { new DateOnly(2026,  9, 24), "추석연휴" },
        { new DateOnly(2026,  9, 25), "추석" },
        { new DateOnly(2026,  9, 26), "추석연휴" },
        { new DateOnly(2026, 10,  3), "개천절" },
        { new DateOnly(2026, 10,  5), "대체공휴일" },
        { new DateOnly(2026, 10,  9), "한글날" },
        { new DateOnly(2026, 12, 25), "크리스마스" },

        // 2027
        { new DateOnly(2027,  1,  1), "신정" },
        { new DateOnly(2027,  2,  7), "설날연휴" },
        { new DateOnly(2027,  2,  8), "설날" },
        { new DateOnly(2027,  2,  9), "설날연휴" },
        { new DateOnly(2027,  3,  1), "삼일절" },
        { new DateOnly(2027,  5,  5), "어린이날" },
        { new DateOnly(2027,  5, 13), "부처님오신날" },
        { new DateOnly(2027,  6,  6), "현충일" },
        { new DateOnly(2027,  6,  7), "대체공휴일" },
        { new DateOnly(2027,  8, 15), "광복절" },
        { new DateOnly(2027,  8, 16), "대체공휴일" },
        { new DateOnly(2027,  9, 13), "추석연휴" },
        { new DateOnly(2027,  9, 14), "추석" },
        { new DateOnly(2027,  9, 15), "추석연휴" },
        { new DateOnly(2027, 10,  3), "개천절" },
        { new DateOnly(2027, 10,  4), "대체공휴일" },
        { new DateOnly(2027, 10,  9), "한글날" },
        { new DateOnly(2027, 12, 25), "크리스마스" },
        { new DateOnly(2027, 12, 27), "대체공휴일" },
    };

    private static readonly string[] DayNames = ["일", "월", "화", "수", "목", "금", "토"];

    // ── 헬퍼 ─────────────────────────────────────────────────────────────────

    private static bool IsHoliday(DateOnly d) =>
        Holidays.ContainsKey(d) || d.DayOfWeek == DayOfWeek.Saturday || d.DayOfWeek == DayOfWeek.Sunday;

    private static bool IsWorkday(DateOnly d) => !IsHoliday(d);

    private static int CountWorkdays(int year, int month)
    {
        var days = DateTime.DaysInMonth(year, month);
        var count = 0;
        for (var i = 1; i <= days; i++)
            if (IsWorkday(new DateOnly(year, month, i))) count++;
        return count;
    }

    private static int CountWorkdaysFrom(DateOnly from, DateOnly to)
    {
        var count = 0;
        var cur = from;
        while (cur <= to)
        {
            if (IsWorkday(cur)) count++;
            cur = cur.AddDays(1);
        }
        return count;
    }

    private static string DayOfWeekKor(DayOfWeek dow) => DayNames[(int)dow];

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

    public Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
    {
        var q     = query.Trim();
        var today = DateOnly.FromDateTime(DateTime.Today);
        var items = new List<LauncherItem>();

        if (string.IsNullOrWhiteSpace(q))
        {
            BuildMonthItems(today.Year, today.Month, today, items);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        var kw = q.ToLowerInvariant();

        // next → 다음 공휴일 5개
        if (kw == "next")
        {
            items.Add(new LauncherItem("다음 공휴일 5개", "D-N일 표시  ·  Enter: 클립보드 복사",
                null, null, Symbol: "\uE787"));
            var upcoming = Holidays.Keys
                .Where(d => d > today)
                .OrderBy(d => d)
                .Take(5);
            foreach (var d in upcoming)
            {
                var diff = d.DayNumber - today.DayNumber;
                var name = Holidays[d];
                var label = $"{d:yyyy-MM-dd} ({DayOfWeekKor(d.DayOfWeek)}) — {name}  D-{diff}일";
                items.Add(new LauncherItem(label, "Enter: 클립보드 복사",
                    null, ("copy", $"{d:yyyy-MM-dd} {name}"), Symbol: "\uE787"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // workdays → 이번달 업무일
        if (kw == "workdays")
        {
            var total     = CountWorkdays(today.Year, today.Month);
            var remaining = CountWorkdaysFrom(today, new DateOnly(today.Year, today.Month,
                DateTime.DaysInMonth(today.Year, today.Month)));
            items.Add(new LauncherItem(
                $"{today.Year}년 {today.Month}월 업무일 — 총 {total}일",
                $"오늘 기준 잔여 업무일: {remaining}일",
                null, ("copy", $"{today.Year}년 {today.Month}월 업무일: 총 {total}일, 잔여 {remaining}일"),
                Symbol: "\uE787"));

            var monthHolidays = Holidays.Where(kv =>
                kv.Key.Year == today.Year && kv.Key.Month == today.Month)
                .OrderBy(kv => kv.Key);
            foreach (var kv in monthHolidays)
            {
                var label = $"🎌 {kv.Key.Day}일 ({DayOfWeekKor(kv.Key.DayOfWeek)}) — {kv.Value}";
                items.Add(new LauncherItem(label, "Enter: 클립보드 복사",
                    null, ("copy", $"{kv.Key:yyyy-MM-dd} {kv.Value}"), Symbol: "\uE787"));
            }
            if (!monthHolidays.Any())
                items.Add(new LauncherItem("이번 달 공휴일 없음", "", null, null, Symbol: "\uE787"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // today → 오늘 정보
        if (kw == "today")
        {
            var isHol = Holidays.TryGetValue(today, out var holName);
            var isWknd = today.DayOfWeek == DayOfWeek.Saturday || today.DayOfWeek == DayOfWeek.Sunday;
            string status;
            if (isHol)       status = $"공휴일 ({holName})";
            else if (isWknd) status = "주말";
            else             status = "평일 (업무일)";

            items.Add(new LauncherItem(
                $"{today:yyyy-MM-dd} ({DayOfWeekKor(today.DayOfWeek)}) — {status}",
                "Enter: 클립보드 복사",
                null, ("copy", $"{today:yyyy-MM-dd} ({DayOfWeekKor(today.DayOfWeek)}) {status}"),
                Symbol: "\uE787"));

            var next = Holidays.Keys.Where(d => d > today).OrderBy(d => d).FirstOrDefault();
            if (next != default)
            {
                var diff = next.DayNumber - today.DayNumber;
                items.Add(new LauncherItem(
                    $"다음 공휴일: {next:yyyy-MM-dd} ({DayOfWeekKor(next.DayOfWeek)}) — {Holidays[next]}  D-{diff}일",
                    "", null, ("copy", $"{next:yyyy-MM-dd} {Holidays[next]}"), Symbol: "\uE787"));
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // yyyy-MM-dd 날짜 직접 조회
        var dateFormats = new[] { "yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd" };
        if (DateOnly.TryParseExact(q, dateFormats, CultureInfo.InvariantCulture,
            DateTimeStyles.None, out var specificDate))
        {
            var isHol  = Holidays.TryGetValue(specificDate, out var hName);
            var isWknd = specificDate.DayOfWeek == DayOfWeek.Saturday ||
                         specificDate.DayOfWeek == DayOfWeek.Sunday;
            string status;
            if (isHol)       status = $"공휴일 ({hName})";
            else if (isWknd) status = "주말";
            else             status = "평일 (업무일)";
            items.Add(new LauncherItem(
                $"{specificDate:yyyy-MM-dd} ({DayOfWeekKor(specificDate.DayOfWeek)}) — {status}",
                "Enter: 클립보드 복사",
                null, ("copy", $"{specificDate:yyyy-MM-dd} ({DayOfWeekKor(specificDate.DayOfWeek)}) {status}"),
                Symbol: "\uE787"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // yyyy-MM or yyyy/MM or yyyyMM 월 조회
        var monthFormats = new[] { "yyyy-MM", "yyyy/MM", "yyyyMM" };
        if (DateOnly.TryParseExact(q + "-01", new[] { "yyyy-MM-dd", "yyyy/MM/dd", "yyyyMM-dd" },
            CultureInfo.InvariantCulture, DateTimeStyles.None, out var monthDate) ||
            TryParseYearMonth(q, out monthDate))
        {
            BuildMonthItems(monthDate.Year, monthDate.Month, today, items);
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 기본 — 이번달
        BuildMonthItems(today.Year, today.Month, today, items);
        return Task.FromResult<IEnumerable<LauncherItem>>(items);
    }

    private static bool TryParseYearMonth(string q, out DateOnly result)
    {
        result = default;
        // yyyy-MM, yyyy/MM, yyyyMM
        var formats = new[] { "yyyy-MM", "yyyy/MM" };
        foreach (var fmt in formats)
        {
            if (DateTime.TryParseExact(q, fmt, CultureInfo.InvariantCulture,
                DateTimeStyles.None, out var dt))
            {
                result = new DateOnly(dt.Year, dt.Month, 1);
                return true;
            }
        }
        // yyyyMM (6자리)
        if (q.Length == 6 && int.TryParse(q[..4], out var y) && int.TryParse(q[4..], out var m)
            && m >= 1 && m <= 12)
        {
            result = new DateOnly(y, m, 1);
            return true;
        }
        return false;
    }

    private static void BuildMonthItems(int year, int month, DateOnly today, List<LauncherItem> items)
    {
        var total     = CountWorkdays(year, month);
        var lastDay   = new DateOnly(year, month, DateTime.DaysInMonth(year, month));
        int remaining;
        if (today.Year == year && today.Month == month)
            remaining = CountWorkdaysFrom(today, lastDay);
        else if (today < new DateOnly(year, month, 1))
            remaining = total;
        else
            remaining = 0;

        var header = $"{year}년 {month}월 · 업무일 {total}일";
        if (today.Year == year && today.Month == month)
            header += $" (잔여 {remaining}일)";

        items.Add(new LauncherItem(header, "공휴일 목록  ·  Enter: 클립보드 복사",
            null, ("copy", header), Symbol: "\uE787"));

        var monthHolidays = Holidays
            .Where(kv => kv.Key.Year == year && kv.Key.Month == month)
            .OrderBy(kv => kv.Key)
            .ToList();

        if (monthHolidays.Count == 0)
        {
            items.Add(new LauncherItem("공휴일 없음", "", null, null, Symbol: "\uE787"));
        }
        else
        {
            foreach (var kv in monthHolidays)
            {
                var label = $"🎌 {kv.Key.Day}일 ({DayOfWeekKor(kv.Key.DayOfWeek)}) — {kv.Value}";
                items.Add(new LauncherItem(label, "Enter: 클립보드 복사",
                    null, ("copy", $"{kv.Key:yyyy-MM-dd} {kv.Value}"), Symbol: "\uE787"));
            }
        }
    }

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        if (item.Data is ("copy", string text))
        {
            try
            {
                Application.Current.Dispatcher.Invoke(() => Clipboard.SetText(text));
                NotificationService.Notify("달력", "클립보드에 복사했습니다.");
            }
            catch { }
        }
        return Task.CompletedTask;
    }
}
