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

namespace AxCopilot.Handlers;

/// <summary>
/// L13-4: 나이·D-day 계산기 핸들러. "age" 프리픽스로 사용합니다.
///
/// 예: age 1990-05-15        → 나이 계산 (만/한국식)
///     age 1990.05.15        → 점 구분자도 지원
///     age 19900515          → 숫자만 입력 (YYYYMMDD)
///     age 2025-12-25        → D-day 계산 (미래 날짜)
///     age next monday       → 다음 월요일까지 D-day
///     age christmas         → 크리스마스까지 D-day
/// Enter → 결과를 클립보드에 복사.
/// </summary>
public class AgeHandler : IActionHandler
{
    public string? Prefix => "age";

    public PluginMetadata Metadata => new(
        "Age",
        "나이·D-day 계산기 — 만 나이 · 한국 나이 · D-day",
        "1.0",
        "AX");

    // 특수 날짜 키워드
    private static readonly Dictionary<string, Func<DateTime, DateTime>> Keywords =
        new(StringComparer.OrdinalIgnoreCase)
        {
            ["christmas"]  = t => new DateTime(t.Month > 12 || (t.Month == 12 && t.Day >= 26) ? t.Year + 1 : t.Year, 12, 25),
            ["xmas"]       = t => new DateTime(t.Month > 12 || (t.Month == 12 && t.Day >= 26) ? t.Year + 1 : t.Year, 12, 25),
            ["newyear"]    = t => new DateTime(t.Year + 1, 1, 1),
            ["new year"]   = t => new DateTime(t.Year + 1, 1, 1),
            ["설날"]        = t => GetNextLunarNewYear(t),
            ["chuseok"]    = t => GetNextChuseok(t),
            ["추석"]        = t => GetNextChuseok(t),
        };

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

        if (string.IsNullOrWhiteSpace(q))
        {
            items.Add(new LauncherItem("나이·D-day 계산기",
                "예: age 1990-05-15  /  age 2025-12-25  /  age christmas",
                null, null, Symbol: "\uE787"));
            items.Add(new LauncherItem("age 1990-01-01",    "생년월일 → 나이",  null, null, Symbol: "\uE787"));
            items.Add(new LauncherItem("age 2025-12-25",    "미래 날짜 D-day",  null, null, Symbol: "\uE787"));
            items.Add(new LauncherItem("age christmas",     "크리스마스 D-day", null, null, Symbol: "\uE787"));
            items.Add(new LauncherItem("age newyear",       "신년 D-day",       null, null, Symbol: "\uE787"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 특수 키워드 확인
        foreach (var (kw, fn) in Keywords)
        {
            if (q.Equals(kw, StringComparison.OrdinalIgnoreCase))
            {
                var targetDate = fn(today);
                items.AddRange(BuildDdayItems(targetDate, kw, today));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
        }

        // 요일 키워드: "next monday"
        if (TryParseNextWeekday(q, out var weekdayDate))
        {
            items.AddRange(BuildDdayItems(weekdayDate, q, today));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 날짜 파싱 시도
        if (!TryParseDate(q, out var date))
        {
            items.Add(new LauncherItem("날짜 형식 오류",
                "예: 1990-05-15  /  1990.05.15  /  19900515  /  2025-12-25",
                null, null, Symbol: "\uE783"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        if (date <= today)
        {
            // 과거 날짜 → 나이/경과 계산
            items.AddRange(BuildAgeItems(date, today));
        }
        else
        {
            // 미래 날짜 → D-day
            items.AddRange(BuildDdayItems(date, date.ToString("yyyy-MM-dd"), today));
        }

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

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

    // ── 나이 계산 ─────────────────────────────────────────────────────────────

    private static IEnumerable<LauncherItem> BuildAgeItems(DateTime birth, DateTime today)
    {
        var ageInt   = CalcAge(birth, today);
        var ageKor   = today.Year - birth.Year + 1;  // 한국식 나이
        var days     = (today - birth).Days;
        var months   = (today.Year - birth.Year) * 12 + (today.Month - birth.Month);
        var nextBirthday = NextBirthday(birth, today);
        var daysToNext   = (nextBirthday - today).Days;

        var summary = $"""
            생년월일: {birth:yyyy-MM-dd}
            만 나이:  {ageInt}세
            한국 나이: {ageKor}세
            경과 일수: {days:N0}일
            경과 개월: {months:N0}개월
            다음 생일: {nextBirthday:yyyy-MM-dd} (D-{daysToNext})
            """;

        yield return new LauncherItem(
            $"만 {ageInt}세  (한국식 {ageKor}세)",
            $"{birth:yyyy-MM-dd}  ·  {days:N0}일 경과  · Enter 복사",
            null, ("copy", summary), Symbol: "\uE787");

        yield return new LauncherItem("만 나이",    $"{ageInt}세",             null, ("copy", ageInt.ToString()),  Symbol: "\uE787");
        yield return new LauncherItem("한국 나이",  $"{ageKor}세",             null, ("copy", ageKor.ToString()),  Symbol: "\uE787");
        yield return new LauncherItem("경과 일수",  $"{days:N0}일",            null, ("copy", days.ToString()),    Symbol: "\uE787");
        yield return new LauncherItem("경과 개월",  $"{months:N0}개월",        null, ("copy", months.ToString()),  Symbol: "\uE787");
        yield return new LauncherItem(
            "다음 생일",
            $"{nextBirthday:yyyy-MM-dd}  ·  D-{daysToNext}",
            null, ("copy", nextBirthday.ToString("yyyy-MM-dd")), Symbol: "\uE787");

        // 요일
        yield return new LauncherItem("태어난 요일", DayKor(birth.DayOfWeek), null, null, Symbol: "\uE787");
    }

    private static IEnumerable<LauncherItem> BuildDdayItems(DateTime target, string label, DateTime today)
    {
        var diff     = (target - today).Days;
        var absDiff  = Math.Abs(diff);
        var dLabel   = diff > 0 ? $"D-{diff}" : diff == 0 ? "D-Day!" : $"D+{absDiff}";

        yield return new LauncherItem(
            dLabel,
            $"{target:yyyy-MM-dd} ({label})  ·  {DayKor(target.DayOfWeek)}",
            null, ("copy", dLabel), Symbol: "\uE787");

        yield return new LauncherItem("날짜",         target.ToString("yyyy-MM-dd"),  null, ("copy", target.ToString("yyyy-MM-dd")), Symbol: "\uE787");
        yield return new LauncherItem("요일",         DayKor(target.DayOfWeek),       null, null, Symbol: "\uE787");
        yield return new LauncherItem("남은 일수",    diff > 0 ? $"{diff:N0}일 후" : diff == 0 ? "오늘!" : $"{absDiff:N0}일 전", null, ("copy", absDiff.ToString()), Symbol: "\uE787");
        yield return new LauncherItem("남은 주",      $"{diff / 7}주 {diff % 7}일",   null, null, Symbol: "\uE787");
    }

    // ── 파싱 헬퍼 ─────────────────────────────────────────────────────────────

    private static bool TryParseDate(string s, out DateTime result)
    {
        result = default;
        s = s.Trim();

        // YYYYMMDD
        if (s.Length == 8 && s.All(char.IsDigit))
        {
            return DateTime.TryParseExact(s, "yyyyMMdd",
                System.Globalization.CultureInfo.InvariantCulture,
                System.Globalization.DateTimeStyles.None, out result);
        }

        // 다양한 구분자 (-, ., /)
        var normalized = s.Replace('.', '-').Replace('/', '-');
        var formats    = new[] { "yyyy-M-d", "yyyy-MM-dd", "yy-M-d", "M-d" };

        foreach (var fmt in formats)
        {
            if (DateTime.TryParseExact(normalized, fmt,
                    System.Globalization.CultureInfo.InvariantCulture,
                    System.Globalization.DateTimeStyles.None, out result))
                return true;
        }

        // "M월 d일" 한국어 형식
        if (normalized.Contains('월'))
        {
            var parts = normalized.Split('월', '일');
            if (parts.Length >= 2 &&
                int.TryParse(parts[0].Trim(), out var m) &&
                int.TryParse(parts[1].Trim(), out var d))
            {
                result = new DateTime(DateTime.Today.Year, m, d);
                return true;
            }
        }

        return false;
    }

    private static bool TryParseNextWeekday(string q, out DateTime result)
    {
        result = default;
        var parts = q.Split(' ', StringSplitOptions.RemoveEmptyEntries);
        if (parts.Length < 2 || !parts[0].Equals("next", StringComparison.OrdinalIgnoreCase))
            return false;

        DayOfWeek? dow = parts[1].ToLowerInvariant() switch
        {
            "monday"    or "mon" or "월" or "월요일" => DayOfWeek.Monday,
            "tuesday"   or "tue" or "화" or "화요일" => DayOfWeek.Tuesday,
            "wednesday" or "wed" or "수" or "수요일" => DayOfWeek.Wednesday,
            "thursday"  or "thu" or "목" or "목요일" => DayOfWeek.Thursday,
            "friday"    or "fri" or "금" or "금요일" => DayOfWeek.Friday,
            "saturday"  or "sat" or "토" or "토요일" => DayOfWeek.Saturday,
            "sunday"    or "sun" or "일" or "일요일" => DayOfWeek.Sunday,
            _ => null,
        };

        if (dow == null) return false;

        var today = DateTime.Today;
        var daysAhead = ((int)dow.Value - (int)today.DayOfWeek + 7) % 7;
        if (daysAhead == 0) daysAhead = 7; // "next"이므로 다음 주
        result = today.AddDays(daysAhead);
        return true;
    }

    // ── 날짜 계산 헬퍼 ────────────────────────────────────────────────────────

    private static int CalcAge(DateTime birth, DateTime today)
    {
        var age = today.Year - birth.Year;
        if (today.Month < birth.Month || (today.Month == birth.Month && today.Day < birth.Day))
            age--;
        return age;
    }

    private static DateTime NextBirthday(DateTime birth, DateTime today)
    {
        var thisYear = new DateTime(today.Year, birth.Month, birth.Day);
        return thisYear >= today ? thisYear : thisYear.AddYears(1);
    }

    // 간략화된 양력 설날 근사값 (실제 음력 계산은 복잡하므로 고정 근사)
    private static DateTime GetNextLunarNewYear(DateTime today)
    {
        // 설날은 대략 1월 말 ~ 2월 초이므로 2월 5일을 기준점으로 사용
        var approx = new DateTime(today.Year, 2, 5);
        return approx >= today ? approx : new DateTime(today.Year + 1, 2, 5);
    }

    private static DateTime GetNextChuseok(DateTime today)
    {
        // 추석은 대략 9월 말 ~ 10월 초이므로 9월 28일을 기준점으로 사용
        var approx = new DateTime(today.Year, 9, 28);
        return approx >= today ? approx : new DateTime(today.Year + 1, 9, 28);
    }

    private static string DayKor(DayOfWeek dow) => dow switch
    {
        DayOfWeek.Monday    => "월요일",
        DayOfWeek.Tuesday   => "화요일",
        DayOfWeek.Wednesday => "수요일",
        DayOfWeek.Thursday  => "목요일",
        DayOfWeek.Friday    => "금요일",
        DayOfWeek.Saturday  => "토요일",
        DayOfWeek.Sunday    => "일요일",
        _                   => "",
    };
}
