from __future__ import annotations

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

from store_schedule_lib.config_loader import (
    DEPRECATED_KINDS,
    load_reminder_rules,
    threshold_int,
    validate_schedule_kinds,
)
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_card_expiring,
    query_followup_due,
    query_member_due,
)
from store_schedule_lib.task_builder import build_cron_task


@dataclass
class ReminderPlan:
    member_id: str
    member_name: str
    reminder_type: str
    title: str
    trigger_at: Optional[str]
    cron_expr: str
    message: str
    recurring: bool = False
    bucket: str = ""
    target_id: str = ""
    appt_id: str = ""
    remind_at: str = ""


def _birthday_plans(members: List[dict]) -> List[ReminderPlan]:
    plans: List[ReminderPlan] = []
    for m in members:
        bday = m.get("birthday")
        if not bday:
            continue
        try:
            month = int(bday.split("-")[1])
            day = int(bday.split("-")[2])
        except (IndexError, ValueError):
            continue
        bucket = bday[:7]
        name = m["name"]
        plans.append(
            ReminderPlan(
                member_id=m["id"],
                member_name=name,
                reminder_type="birthday",
                title="{} 生日提醒".format(name),
                trigger_at=None,
                cron_expr="0 9 {} {} *".format(day, month),
                message=format_message("birthday", name=name),
                recurring=True,
                bucket=bucket,
                target_id=m["id"],
            )
        )
    return plans


def _member_due_plans_from_query(conn: sqlite3.Connection, days: int) -> List[ReminderPlan]:
    today = date.today().isoformat()
    items = query_member_due(conn, today, days=days)
    plans: List[ReminderPlan] = []
    for it in items:
        vd = it.get("due_date", "")
        try:
            d = date.fromisoformat(vd[:10])
            td = d - timedelta(days=1)
        except ValueError:
            continue
        name = it.get("name", "")
        mid = it.get("member_id", "")
        plans.append(
            ReminderPlan(
                member_id=mid,
                member_name=name,
                reminder_type="member_due",
                title="{} 到期提醒".format(name),
                trigger_at=vd,
                cron_expr="0 9 {} {} *".format(td.day, td.month),
                message=format_message("member_due", name=name, date=vd),
                recurring=False,
                bucket=vd[:10],
                target_id=mid,
            )
        )
    return plans


def _card_expiring_plans_from_query(conn: sqlite3.Connection, days: int) -> List[ReminderPlan]:
    today = date.today().isoformat()
    items = query_card_expiring(conn, today, days=days)
    plans: List[ReminderPlan] = []
    for it in items:
        ed = it.get("expire_date", "")
        try:
            d = date.fromisoformat(ed[:10])
            td = d - timedelta(days=1)
        except ValueError:
            continue
        plans.append(
            ReminderPlan(
                member_id=it.get("member_id", ""),
                member_name=it.get("member_name", ""),
                reminder_type="card_expiring",
                title="{} 次卡到期提醒".format(it.get("member_name", "")),
                trigger_at=ed,
                cron_expr="0 9 {} {} *".format(td.day, td.month),
                message=format_message(
                    "card_expiring",
                    member_name=it.get("member_name", ""),
                    card_name=it.get("name", ""),
                    expire_date=ed,
                ),
                recurring=False,
                bucket="{}:{}".format(it.get("member_id"), it.get("card_id")),
                target_id=str(it.get("card_id", "")),
            )
        )
    return plans


def _followup_plans_from_query(conn: sqlite3.Connection) -> List[ReminderPlan]:
    today = date.today().isoformat()
    items = query_followup_due(conn, today)
    plans: List[ReminderPlan] = []
    for it in items:
        fd = it.get("next_followup_date", "")
        try:
            d = date.fromisoformat(fd[:10])
        except ValueError:
            continue
        name = it.get("member_name", "")
        mid = it.get("member_id", "")
        plans.append(
            ReminderPlan(
                member_id=mid,
                member_name=name,
                reminder_type="follow_up",
                title="{} 跟进提醒".format(name),
                trigger_at=fd,
                cron_expr="0 9 {} {} *".format(d.day, d.month),
                message=format_message("follow_up", name=name),
                recurring=False,
                bucket=fd[:10],
                target_id=mid,
            )
        )
    return plans


def build_plans(conn: sqlite3.Connection, kinds: List[str], payload: Dict[str, Any]) -> List[ReminderPlan]:
    err = validate_schedule_kinds(kinds)
    if err == "reminder_kind_deprecated":
        raise ValueError("reminder_kind_deprecated")
    if err == "invalid_reminder_kind":
        raise ValueError("invalid_reminder_kind")

    for k in kinds:
        if k in DEPRECATED_KINDS:
            raise ValueError("reminder_kind_deprecated")

    members = [dict(r) for r in conn.execute("SELECT * FROM store_members WHERE status='active'")]
    plans: List[ReminderPlan] = []

    if "birthday" in kinds:
        plans.extend(_birthday_plans(members))
    if "member_due" in kinds:
        days = int(payload.get("member_due_days", threshold_int(conn, "member_due_days", 7)))
        plans.extend(_member_due_plans_from_query(conn, days))
    if "card_expiring" in kinds:
        days = int(payload.get("card_expiring_days", threshold_int(conn, "card_expiring_days", 7)))
        plans.extend(_card_expiring_plans_from_query(conn, days))
    if "follow_up" in kinds:
        plans.extend(_followup_plans_from_query(conn))

    fu = payload.get("follow_up")
    if fu and fu.get("match"):
        match = fu["match"]
        mid = match.get("member_id")
        name = match.get("name")
        row = None
        if mid:
            row = conn.execute("SELECT * FROM store_members WHERE id=?", (mid,)).fetchone()
        elif name:
            row = conn.execute(
                "SELECT * FROM store_members WHERE name=? AND status='active' LIMIT 1", (name,)
            ).fetchone()
        if row:
            fd = fu.get("date") or date.today().isoformat()
            d = date.fromisoformat(fd[:10])
            plans.append(
                ReminderPlan(
                    member_id=row["id"],
                    member_name=row["name"],
                    reminder_type="follow_up",
                    title="{} 跟进提醒".format(row["name"]),
                    trigger_at=fd,
                    cron_expr="0 9 {} {} *".format(d.day, d.month),
                    message=format_message("follow_up", name=row["name"]),
                    recurring=False,
                    bucket=fd[:10],
                    target_id=row["id"],
                )
            )

    if "appt" in kinds:
        from store_schedule_lib.daily_plan import build_appt_plans

        span = int(payload.get("appt_days", 1))
        plans.extend(build_appt_plans(conn, date.today().isoformat(), days=span))

    return plans


def _ensure_daily_plan_cron(dry_run: bool) -> Dict[str, Any]:
    rules = load_reminder_rules()
    scan = rules.get("scan", {})
    cron_expr = scan.get("daily_plan_cron", "45 7 * * *")
    tz = scan.get("tz", "Asia/Shanghai")
    task_name = "store-daily-plan"
    payload = build_cron_task(
        task_name,
        cron_expr,
        "store-schedule daily-plan --register-appt-only",
        tz=tz,
        delete_after_run=False,
    )
    if dry_run:
        return {"task_name": task_name, "scheduled": True, "dry_run": True, "mode": "daily_plan"}
    scheduled = register_job(payload)
    return {"task_name": task_name, "scheduled": scheduled, "mode": "daily_plan"}


def schedule_plans(
    conn: sqlite3.Connection,
    plans: List[ReminderPlan],
    *,
    dry_run: bool = False,
    register_appt: bool = True,
) -> List[Dict[str, Any]]:
    results: List[Dict[str, Any]] = []
    daily_meta = _ensure_daily_plan_cron(dry_run)
    results.append({
        "reminder_type": "cron_registered",
        "target_id": "daily_plan",
        **daily_meta,
    })

    if register_appt:
        from store_schedule_lib.daily_plan import run_daily_plan

        appt_results = run_daily_plan(
            conn, date.today().isoformat(), days=1, dry_run=dry_run, appt_only=True,
        )
        results.extend(appt_results.get("items", []))

    for plan in plans:
        if plan.reminder_type == "appt":
            continue
        results.append({
            **asdict(plan),
            "scheduled": daily_meta.get("scheduled", False),
            "skipped": "handled_by_daily_digest",
            "mode": "daily_plan",
        })
    return results
