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

namespace AxCopilot.Handlers;

/// <summary>
/// L25-2: 오늘 특정 시각 알림. "remind" 프리픽스로 사용합니다.
///
/// 예: remind                 → 오늘 등록된 알림 목록
///     remind 15:00 보고서 제출 → 오후 3시에 알림
///     remind 오후3시 팀장보고  → 한국어 시각 파싱
///     remind del 1           → 1번 알림 취소
///     remind clear           → 지난 알림 정리
/// </summary>
public class RemindHandler : IActionHandler
{
    public string? Prefix => "remind";

    public PluginMetadata Metadata => new(
        "알림",
        "오늘 특정 시각 알림 — 시간 설정 · 취소 · 목록",
        "1.0",
        "AX");

    private sealed class RemindEntry
    {
        public int                   Id      { get; set; }
        public DateTime              Time    { get; set; }
        public string                Message { get; set; } = "";
        public bool                  Fired   { get; set; }
        public CancellationTokenSource? Cts  { get; set; }
    }

    private static readonly List<RemindEntry>                      _reminders = [];
    private static readonly Dictionary<int, CancellationTokenSource> _ctsDic  = [];
    private static readonly object                                   _lock     = new();
    private static int                                               _nextId   = 1;

    // 외부(TodayHandler)에서 오늘 알림 목록 조회용
    internal static List<(DateTime Time, string Message)> GetTodayReminders()
    {
        var today = DateTime.Today;
        lock (_lock)
        {
            return _reminders
                .Where(r => !r.Fired && r.Time.Date == today)
                .Select(r => (r.Time, r.Message))
                .OrderBy(r => r.Time)
                .ToList();
        }
    }

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

        if (string.IsNullOrWhiteSpace(q))
        {
            List<RemindEntry> pending;
            lock (_lock)
                pending = _reminders.Where(r => !r.Fired).OrderBy(r => r.Time).ToList();

            if (pending.Count == 0)
            {
                items.Add(new LauncherItem("등록된 알림 없음",
                    "remind HH:mm 메시지  또는  remind 오후3시 메시지",
                    null, null, Symbol: "\uE787"));
            }
            else
            {
                items.Add(new LauncherItem($"알림 {pending.Count}개",
                    "remind <시각> <메시지>  /  remind del <번호>",
                    null, null, Symbol: "\uE787"));
                foreach (var r in pending)
                {
                    var left = r.Time > DateTime.Now
                        ? FormatLeft(r.Time - DateTime.Now)
                        : "시간 지남";
                    items.Add(new LauncherItem(
                        $"#{r.Id}  {r.Time:HH:mm}  —  {r.Message}",
                        $"남은 시간: {left}  ·  Enter로 취소",
                        null, ("del", r.Id.ToString()), Symbol: "\uE787"));
                }
            }
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

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

        // del 명령
        if (sub is "del" or "cancel" or "취소")
        {
            var idStr = parts.Length > 1 ? parts[1].Trim() : "";
            if (!int.TryParse(idStr, out var delId))
            {
                items.Add(new LauncherItem("번호를 입력하세요",
                    "예: remind del 1",
                    null, null, Symbol: "\uE783"));
                return Task.FromResult<IEnumerable<LauncherItem>>(items);
            }
            RemindEntry? target;
            lock (_lock) target = _reminders.FirstOrDefault(r => r.Id == delId);
            if (target == null)
                items.Add(new LauncherItem($"#{delId} 알림을 찾을 수 없습니다", "",
                    null, null, Symbol: "\uE783"));
            else
                items.Add(new LauncherItem(
                    $"#{delId} 알림 취소: {target.Time:HH:mm}  {target.Message}",
                    "Enter로 취소합니다",
                    null, ("del", delId.ToString()), Symbol: "\uE787"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // clear 명령
        if (sub == "clear")
        {
            items.Add(new LauncherItem("지난 알림 정리",
                "발화된 알림을 목록에서 제거합니다  ·  Enter 실행",
                null, ("clear", ""), Symbol: "\uE787"));
            return Task.FromResult<IEnumerable<LauncherItem>>(items);
        }

        // 시각 파싱 시도
        var (timeStr, msgStr) = SplitTimeAndMessage(q);
        if (!string.IsNullOrWhiteSpace(timeStr) && TryParseTime(timeStr, out var alarmTime))
        {
            var leftText = alarmTime > DateTime.Now
                ? FormatLeft(alarmTime - DateTime.Now)
                : "이미 지난 시각 (내일로 설정됩니다)";
            var message  = string.IsNullOrWhiteSpace(msgStr) ? "(메시지 없음)" : msgStr;
            var encoded  = $"{alarmTime:O}|{message}";
            items.Add(new LauncherItem(
                $"알림 설정: {alarmTime:HH:mm}  —  {message}",
                $"{leftText}  ·  Enter로 설정",
                null, ("set", encoded), Symbol: "\uE787"));
        }
        else
        {
            items.Add(new LauncherItem($"시각 파싱 실패: '{q}'",
                "예: remind 15:00 보고서 제출  /  remind 오후3시 팀장보고",
                null, null, Symbol: "\uE783"));
        }

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

    public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
    {
        switch (item.Data)
        {
            case ("set", string encoded):
            {
                var idx = encoded.IndexOf('|');
                if (idx < 0) break;
                var timeIso = encoded[..idx];
                var message = encoded[(idx + 1)..];
                if (!DateTime.TryParse(timeIso, null,
                    System.Globalization.DateTimeStyles.RoundtripKind, out var alarmTime))
                    break;

                var cts = new CancellationTokenSource();
                int id;
                lock (_lock)
                {
                    id = _nextId++;
                    var entry = new RemindEntry { Id = id, Time = alarmTime, Message = message, Cts = cts };
                    _reminders.Add(entry);
                    _ctsDic[id] = cts;
                }
                NotificationService.Notify("알림", $"#{id} {alarmTime:HH:mm}  {message}  설정됨");
                _ = RunReminderAsync(id, alarmTime, message, cts.Token);
                break;
            }

            case ("del", string idStr) when int.TryParse(idStr, out var delId):
            {
                RemindEntry? entry;
                lock (_lock)
                {
                    entry = _reminders.FirstOrDefault(r => r.Id == delId);
                    if (entry != null) _reminders.Remove(entry);
                    if (_ctsDic.TryGetValue(delId, out var c)) { c.Cancel(); _ctsDic.Remove(delId); }
                }
                if (entry != null)
                    NotificationService.Notify("알림", $"#{delId} 알림 취소됨");
                break;
            }

            case ("clear", _):
            {
                int cleared;
                lock (_lock)
                {
                    cleared = _reminders.RemoveAll(r => r.Fired);
                }
                NotificationService.Notify("알림", $"지난 알림 {cleared}개 정리됨");
                break;
            }
        }
        return Task.CompletedTask;
    }

    // ── 알림 실행 ────────────────────────────────────────────────────────────

    private static async Task RunReminderAsync(int id, DateTime at, string message, CancellationToken token)
    {
        try
        {
            var delay = at - DateTime.Now;
            if (delay > TimeSpan.Zero)
                await Task.Delay(delay, token);

            lock (_lock)
            {
                var entry = _reminders.FirstOrDefault(r => r.Id == id);
                if (entry != null) entry.Fired = true;
                _ctsDic.Remove(id);
            }
            NotificationService.Notify("⏰ 알림", $"{at:HH:mm}  {message}");
        }
        catch (OperationCanceledException) { }
    }

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

    private static (string timeStr, string message) SplitTimeAndMessage(string q)
    {
        var sp = q.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
        if (sp.Length == 0) return ("", "");
        return (sp[0], sp.Length > 1 ? sp[1] : "");
    }

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

        // HH:mm 또는 H:mm
        if (System.Text.RegularExpressions.Regex.IsMatch(s, @"^\d{1,2}:\d{2}$"))
        {
            var colonParts = s.Split(':');
            if (int.TryParse(colonParts[0], out var h) &&
                int.TryParse(colonParts[1], out var m) &&
                h is >= 0 and <= 23 && m is >= 0 and <= 59)
            {
                result = DateTime.Today.AddHours(h).AddMinutes(m);
                if (result <= DateTime.Now) result = result.AddDays(1);
                return true;
            }
        }

        // 한국어 시각: 오전N시M분, 오후N시M분, N시M분, 오전N시, 오후N시, N시
        var korMatch = System.Text.RegularExpressions.Regex.Match(s,
            @"^(오전|오후)?(\d{1,2})시((\d{1,2})분)?$");
        if (korMatch.Success)
        {
            var meridiem = korMatch.Groups[1].Value;
            if (!int.TryParse(korMatch.Groups[2].Value, out var h)) return false;
            var minStr = korMatch.Groups[4].Value;
            var m = string.IsNullOrEmpty(minStr) ? 0 : int.TryParse(minStr, out var mp) ? mp : 0;

            if (meridiem == "오후" && h < 12) h += 12;
            if (meridiem == "오전" && h == 12) h = 0;

            if (h is < 0 or > 23 || m is < 0 or > 59) return false;

            result = DateTime.Today.AddHours(h).AddMinutes(m);
            if (result <= DateTime.Now) result = result.AddDays(1);
            return true;
        }

        return false;
    }

    private static string FormatLeft(TimeSpan ts)
    {
        if (ts.TotalSeconds < 60)
            return $"{(int)ts.TotalSeconds}초 후";
        if (ts.TotalMinutes < 60)
            return $"{(int)ts.TotalMinutes}분 후";
        var h = (int)ts.TotalHours;
        var m = (int)(ts.TotalMinutes - h * 60);
        return m > 0 ? $"{h}시간 {m}분 후" : $"{h}시간 후";
    }
}
