"""
日程变化检测。

对比两份事件快照，输出新增、修改、取消列表。

核心规则：
  - 日程自然结束后消失  → 不通知
  - 日程被取消          → 通知（status=cancelled）
  - 标题或时间有变化    → 通知（修改）
  - 无任何变化          → 不通知
"""

from __future__ import annotations

import logging
import sys
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path
from typing import Callable

sys.path.insert(0, str(Path(__file__).parent.parent))

from gcal.models import CalendarEvent

logger = logging.getLogger(__name__)


@dataclass
class EventDiffResult:
    added:    list[CalendarEvent]                              = field(default_factory=list)
    removed:  list[CalendarEvent]                              = field(default_factory=list)
    modified: list[tuple[CalendarEvent, CalendarEvent]]        = field(default_factory=list)

    @property
    def has_changes(self) -> bool:
        return bool(self.added or self.removed or self.modified)

    @property
    def summary_text(self) -> str:
        parts = []
        if self.added:    parts.append(f"新增 {len(self.added)} 个")
        if self.removed:  parts.append(f"取消 {len(self.removed)} 个")
        if self.modified: parts.append(f"修改 {len(self.modified)} 个")
        return "、".join(parts) if parts else "无变化"

    def format_notification(self) -> str:
        """生成发送给 用户 的通知文本。"""
        lines = ["📅 **Google Calendar 日程变化通知**", ""]

        if self.added:
            lines.append("**新增日程：**")
            for e in self.added[:5]:
                lines.append(f"• {e.summary} — {_fmt_time(e.start)}")
            if len(self.added) > 5:
                lines.append(f"  还有 {len(self.added) - 5} 个…")
            lines.append("")

        if self.removed:
            lines.append("**已取消：**")
            for e in self.removed[:5]:
                lines.append(f"• {e.summary} — {_fmt_time(e.start)}")
            if len(self.removed) > 5:
                lines.append(f"  还有 {len(self.removed) - 5} 个…")
            lines.append("")

        if self.modified:
            lines.append("**已修改：**")
            for prev, cur in self.modified[:3]:
                if prev.summary != cur.summary:
                    lines.append(f"• 标题：{prev.summary} → {cur.summary}")
                else:
                    lines.append(
                        f"• {cur.summary}：{_fmt_time(prev.start)} → {_fmt_time(cur.start)}"
                    )
            if len(self.modified) > 3:
                lines.append(f"  还有 {len(self.modified) - 3} 个…")

        return "\n".join(lines)


def diff_events(
    current: list[CalendarEvent],
    last: list[CalendarEvent],
    get_remote_status: Callable[[str], str | None] | None = None,
    now: datetime | None = None,
) -> EventDiffResult:
    """
    对比当前事件列表与上次快照。

    get_remote_status：
      可选回调，传入 event_id，返回 'confirmed' | 'cancelled' | None。
      传入时使用 API 查询精确判断取消/自然结束（推荐）。
      不传时退化为时间判断（event.is_past → 不报告）。
    """
    if now is None:
        now = datetime.now(timezone.utc)

    cur_map  = {e.id: e for e in current if e.id}
    last_map = {e.id: e for e in last    if e.id}

    added:    list[CalendarEvent]                       = []
    removed:  list[CalendarEvent]                       = []
    modified: list[tuple[CalendarEvent, CalendarEvent]] = []

    # 新增
    for eid, event in cur_map.items():
        if eid not in last_map:
            added.append(event)

    # 消失：区分取消 vs 自然结束
    for eid, event in last_map.items():
        if eid not in cur_map:
            if get_remote_status is not None:
                status = get_remote_status(eid)
                logger.debug(f"事件 {eid}（{event.summary}）远端状态: {status}")
                if status == "cancelled":
                    removed.append(event)
                else:
                    logger.info(f"'{event.summary}' 已自然结束，不通知")
            else:
                # 降级：用时间判断
                if not event.is_past(now):
                    removed.append(event)
                else:
                    logger.info(f"'{event.summary}' 已结束，不通知")

    # 修改（标题、开始时间、结束时间）
    for eid in cur_map.keys() & last_map.keys():
        if _is_modified(cur_map[eid], last_map[eid]):
            modified.append((last_map[eid], cur_map[eid]))

    return EventDiffResult(added=added, removed=removed, modified=modified)


# ────────────────────────── 内部工具 ─────────────────────────────

def _is_modified(cur: CalendarEvent, prev: CalendarEvent) -> bool:
    if cur.summary != prev.summary:
        return True

    def to_utc(dt: datetime) -> datetime:
        if dt.tzinfo is None:
            return dt.replace(tzinfo=timezone.utc)
        return dt.astimezone(timezone.utc)

    if to_utc(cur.start) != to_utc(prev.start):
        return True
    if to_utc(cur.end) != to_utc(prev.end):
        return True
    return False


def _fmt_time(dt: datetime) -> str:
    import sys
    from pathlib import Path
    sys.path.insert(0, str(Path(__file__).parent.parent))
    from compat import ZoneInfo, make_aware
    bj = dt.astimezone(ZoneInfo("Asia/Shanghai"))
    return bj.strftime("%m月%d日 %H:%M")
