#!/usr/bin/env python3
"""
ROC（Regular Operation Calendar）定期事件提醒检查。

每天自动运行，检查今日是否有需要提醒的 ROC 事件，输出 JSON 供 Agent 处理。

用法：
  python3 apps/check_roc.py                # 检查今天，输出 JSON
  python3 apps/check_roc.py --upcoming 30  # 列出未来 30 天内的提醒
  python3 apps/check_roc.py --list         # 列出今年全部事件及提醒日期
  python3 apps/check_roc.py --dry-run      # 只计算，不写状态文件
"""

from __future__ import annotations

import argparse
import json
import re
import sys
from datetime import datetime, timedelta
from pathlib import Path

import yaml

# ── 路径配置 ──────────────────────────────────────────────────────────
_SKILL_BASE = Path(__file__).resolve().parent.parent
_CONFIG_FILE = _SKILL_BASE / "config" / "roc_events.yaml"
_STATE_FILE  = _SKILL_BASE / "data" / "roc_state.json"

sys.path.insert(0, str(_SKILL_BASE))

from config.settings import TIMEZONE
from services.calendar_backend import active_provider
from services.datetime_utils import current_date_str, current_year, now_local


# ── 日期计算 ──────────────────────────────────────────────────────────

def _nth_weekday(year: int, month: int, weekday: int, n: int) -> datetime:
    """返回 year/month 中第 n 个 weekday（0=周一…6=周日）。"""
    first = datetime(year, month, 1)
    offset = (weekday - first.weekday()) % 7
    first_match = first + timedelta(days=offset)
    return first_match + timedelta(weeks=n - 1)


_WEEKDAY_MAP = {"周一": 0, "周二": 1, "周三": 2, "周四": 3, "周五": 4, "周六": 5, "周日": 6}


def resolve_event_date(rule: str, year: int, spring: dict) -> datetime | None:
    """将 rule 字符串解析为具体日期。"""

    # 每年M月D日
    m = re.fullmatch(r"每年(\d+)月(\d+)日", rule.strip())
    if m:
        try:
            return datetime(year, int(m.group(1)), int(m.group(2)))
        except ValueError:
            return None

    # 每年M月第N个周X
    m = re.search(r"每年(\d+)月第(\d+)个(周[一二三四五六日])", rule)
    if m:
        month   = int(m.group(1))
        n       = int(m.group(2))
        weekday = _WEEKDAY_MAP.get(m.group(3))
        if weekday is not None:
            return _nth_weekday(year, month, weekday, n)

    # 春节相关（从 spring_festival 表查）
    sf = spring.get(year, {})
    if "春节前最后工作日" in rule:
        d = sf.get("last_workday")
        return datetime.strptime(d, "%Y-%m-%d") if d else None
    if "春节后第一个工作日" in rule:
        d = sf.get("first_workday")
        return datetime.strptime(d, "%Y-%m-%d") if d else None

    # 5.1 假期后第一个工作日（固定 5-06，如变动手工在 YAML 配置）
    if "5.1假期后第一个工作日" in rule:
        return datetime(year, 5, 6)

    return None


# ── 数据加载 ──────────────────────────────────────────────────────────

def _load_config() -> dict:
    with open(_CONFIG_FILE, encoding="utf-8") as f:
        return yaml.safe_load(f)


def _load_state() -> dict:
    try:
        return json.loads(_STATE_FILE.read_text(encoding="utf-8"))
    except Exception:
        return {}


def _save_state(state: dict) -> None:
    _STATE_FILE.parent.mkdir(parents=True, exist_ok=True)
    _STATE_FILE.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")


# ── 核心逻辑 ──────────────────────────────────────────────────────────

def build_year_events(year: int, cfg: dict) -> list[dict]:
    """计算指定年份所有 ROC 事件的发生日期和提醒日期。"""
    spring = cfg.get("spring_festival", {})
    result = []
    for ev in cfg.get("events", []):
        event_dt = resolve_event_date(ev["rule"], year, spring)
        if event_dt is None:
            continue
        lead = int(ev.get("lead_days", 0))
        reminder_dt = event_dt - timedelta(days=lead)
        result.append({
            "id":            ev["id"],
            "name":          ev["name"],
            "rule":          ev["rule"],
            "event_date":    event_dt.strftime("%Y-%m-%d"),
            "reminder_date": reminder_dt.strftime("%Y-%m-%d"),
            "reminder_time": ev.get("reminder_time", "08:00"),
            "lead_days":     lead,
            "description":   ev.get("description", ""),
        })
    return result


def check_today(year_events: list[dict], today: str, state: dict) -> list[dict]:
    """返回今天需要提醒且尚未发送过提醒的事件。"""
    hits = []
    for ev in year_events:
        if ev["reminder_date"] != today:
            continue
        # 若同一事件今天已提醒过，跳过
        if state.get(ev["id"]) == today:
            continue
        hits.append(ev)
    return hits


def upcoming_events(year_events: list[dict], today: str, days: int) -> list[dict]:
    """返回未来 days 天内（含今天）提醒日期内的事件，按提醒日期升序。"""
    today_dt  = datetime.strptime(today, "%Y-%m-%d")
    cutoff_dt = today_dt + timedelta(days=days)
    result = []
    for ev in year_events:
        rd = datetime.strptime(ev["reminder_date"], "%Y-%m-%d")
        if today_dt <= rd <= cutoff_dt:
            ev = dict(ev)
            ev["days_until_reminder"] = (rd - today_dt).days
            ed = datetime.strptime(ev["event_date"], "%Y-%m-%d")
            ev["days_until_event"] = (ed - today_dt).days
            result.append(ev)
    result.sort(key=lambda x: x["reminder_date"])
    return result


def format_reminder_message(ev: dict) -> str:
    ed = datetime.strptime(ev["event_date"], "%Y-%m-%d")
    today = now_local(TIMEZONE).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None)
    days = (ed - today).days
    lines = [
        f"📅 **ROC 提醒：{ev['name']}**",
        f"📆 事件日期：{ev['event_date']}（{days} 天后）",
    ]
    if ev.get("description"):
        lines.append(f"📝 {ev['description']}")
    return "\n".join(lines)


# ── CLI 入口 ──────────────────────────────────────────────────────────

def main() -> None:
    parser = argparse.ArgumentParser(description="ROC 定期事件提醒检查")
    parser.add_argument("--upcoming", type=int, metavar="DAYS",
                        help="列出未来 N 天内即将提醒的事件")
    parser.add_argument("--list", action="store_true",
                        help="列出今年全部 ROC 事件（事件日期 + 提醒日期）")
    parser.add_argument("--dry-run", action="store_true",
                        help="只计算，不写状态文件")
    parser.add_argument("--date", default=None,
                        help="模拟日期 YYYY-MM-DD（默认今天，测试用）")
    args = parser.parse_args()

    today = args.date or current_date_str(TIMEZONE)
    year  = int(today[:4])

    cfg         = _load_config()
    year_events = build_year_events(year, cfg)
    state       = _load_state()

    # 当年 ROC 事件若尚未同步到 Google Calendar，自动触发一次
    if active_provider() == "google":
        try:
            from apps.sync_roc_to_gcal import is_year_synced, sync_year
            if not is_year_synced(year):
                print(f"[check_roc] {year} 年 ROC 事件未同步到 GCal，自动同步中…", flush=True)
                result = sync_year(year, verbose=False)
                added = len(result.get("added", []))
                print(f"[check_roc] GCal 同步完成：新增 {added} 个全天事件", flush=True)
        except Exception as e:
            # 同步失败不影响提醒功能
            print(f"[check_roc] GCal 同步跳过（{e}）", flush=True)

    # ── --list ────────────────────────────────────────────────────────
    if args.list:
        out = {"year": year, "events": year_events}
        print(json.dumps(out, ensure_ascii=False, indent=2))
        return

    # ── --upcoming ────────────────────────────────────────────────────
    if args.upcoming is not None:
        evs = upcoming_events(year_events, today, args.upcoming)
        print(json.dumps({"today": today, "upcoming": evs}, ensure_ascii=False, indent=2))
        return

    # ── 默认：检查今天 ────────────────────────────────────────────────
    hits = check_today(year_events, today, state)

    output = {
        "ok":        True,
        "today":     today,
        "reminders": [],
    }

    for ev in hits:
        output["reminders"].append({
            "id":          ev["id"],
            "name":        ev["name"],
            "event_date":  ev["event_date"],
            "description": ev["description"],
            "message":     format_reminder_message(ev),
        })

    if not args.dry_run and hits:
        for ev in hits:
            state[ev["id"]] = today
        _save_state(state)

    print(json.dumps(output, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
