from __future__ import annotations

import sqlite3
from dataclasses import asdict
from datetime import date, datetime, timedelta, timezone
from typing import Any, Dict, List, Optional

try:
    from zoneinfo import ZoneInfo
except ImportError:
    from backports.zoneinfo import ZoneInfo  # type: ignore

UTC = timezone.utc

from store_schedule_lib.config_loader import (
    lead_minutes_for,
    load_recurring_events,
    load_reminder_rules,
)
from store_schedule_lib.cron_registry import register_job
from store_schedule_lib.message_templates import format_message
from store_schedule_lib.queries import query_appt_range
from store_schedule_lib.task_builder import build_reminder_task, build_reminder_task_name


def _parse_start(start_dt: str) -> Optional[datetime]:
    try:
        return datetime.strptime(start_dt, "%Y-%m-%d %H:%M")
    except ValueError:
        return None


def build_appt_plans(
    conn: sqlite3.Connection,
    start_date: str,
    *,
    days: int = 1,
) -> List[Any]:
    from store_schedule_lib.reminder_planner import ReminderPlan

    end = (datetime.strptime(start_date, "%Y-%m-%d") + timedelta(days=days - 1)).strftime("%Y-%m-%d")
    items = query_appt_range(conn, start_date, end)
    lead = lead_minutes_for("appt")
    tz_name = load_reminder_rules().get("scan", {}).get("tz", "Asia/Shanghai")
    tz = ZoneInfo(tz_name)
    plans: List[ReminderPlan] = []
    for it in items:
        start = _parse_start(it.get("start_dt", ""))
        if not start:
            continue
        start_aware = start.replace(tzinfo=tz)
        remind_at = start_aware - timedelta(minutes=lead)
        bucket = remind_at.astimezone(UTC).strftime("%Y%m%dT%H%M%SZ")
        plans.append(
            ReminderPlan(
                member_id=it.get("member_id", ""),
                member_name=it.get("member_name", ""),
                reminder_type="appt",
                title="预约 {}".format(it.get("member_name", "")),
                trigger_at=it.get("start_dt"),
                cron_expr="",
                message=format_message(
                    "appt",
                    member_name=it.get("member_name", ""),
                    service=it.get("service", ""),
                    start_dt=it.get("start_dt", ""),
                    lead=lead,
                ),
                recurring=False,
                bucket=bucket,
                target_id=str(it.get("appt_id", "")),
                appt_id=str(it.get("appt_id", "")),
                remind_at=remind_at.isoformat(),
            )
        )
    return plans


def _register_appt_plan(conn: sqlite3.Connection, plan: Any, *, dry_run: bool, tz_name: str) -> Dict[str, Any]:
    from store_schedule_lib.reminder_planner import ReminderPlan

    assert isinstance(plan, ReminderPlan)
    tid = plan.appt_id or plan.target_id

    start = _parse_start(plan.trigger_at or "")
    if not start:
        return {**asdict(plan), "scheduled": False, "skipped": "invalid_start", "reminder_type": "appt"}

    lead = lead_minutes_for("appt")
    tz = ZoneInfo(tz_name)
    start_aware = start.replace(tzinfo=tz)
    remind_at = start_aware - timedelta(minutes=lead)
    bucket = plan.bucket or remind_at.astimezone(UTC).strftime("%Y%m%dT%H%M%SZ")
    row = conn.execute(
        "SELECT 1 FROM sent_reminders WHERE reminder_type=? AND target_id=? AND bucket=?",
        ("appt", tid, bucket),
    ).fetchone()
    if row:
        return {**asdict(plan), "scheduled": False, "skipped": "already_sent", "reminder_type": "appt"}
    now = datetime.now(tz)
    if remind_at < now:
        return {**asdict(plan), "scheduled": False, "skipped": "past", "reminder_type": "appt"}

    task_name = build_reminder_task_name("appt", tid, remind_at.astimezone(UTC))
    if dry_run:
        return {
            **asdict(plan),
            "task_name": task_name,
            "scheduled": True,
            "dry_run": True,
            "reminder_type": "appt",
        }

    payload = build_reminder_task(task_name, remind_at.astimezone(UTC), plan.message)
    scheduled = register_job(payload)
    if scheduled:
        conn.execute(
            "INSERT OR IGNORE INTO sent_reminders (reminder_type, target_id, bucket, note) VALUES (?,?,?,?)",
            ("appt", tid, bucket, plan.title),
        )
    return {
        **asdict(plan),
        "task_name": task_name,
        "scheduled": scheduled,
        "reminder_type": "appt",
    }


def _recurring_event_lines(date_str: str) -> List[str]:
    lines: List[str] = []
    for ev in load_recurring_events():
        name = ev.get("name", "")
        desc = ev.get("description", "")
        if name:
            lines.append("- {}：{}".format(name, desc or "周期事件"))
    return lines


def run_daily_plan(
    conn: sqlite3.Connection,
    date_str: str,
    *,
    days: int = 1,
    dry_run: bool = False,
    appt_only: bool = False,
) -> Dict[str, Any]:
    rules = load_reminder_rules()
    tz_name = rules.get("scan", {}).get("tz", "Asia/Shanghai")
    items: List[Dict[str, Any]] = []

    appt_plans = build_appt_plans(conn, date_str, days=days)
    for plan in appt_plans:
        items.append(_register_appt_plan(conn, plan, dry_run=dry_run, tz_name=tz_name))

    md_parts = ["# 门店每日计划（{}）".format(date_str), ""]
    if appt_plans:
        md_parts.append("## 预约提醒")
        for p in appt_plans:
            md_parts.append("- {} {} → {}".format(p.member_name, p.trigger_at, p.remind_at))
        md_parts.append("")

    if not appt_only:
        recurring = _recurring_event_lines(date_str)
        if recurring:
            md_parts.append("## 周期经营事件")
            md_parts.extend(recurring)

    return {
        "date": date_str,
        "days": days,
        "dry_run": dry_run,
        "count": len(items),
        "items": items,
        "markdown": "\n".join(md_parts).strip(),
    }
