"""System settings and reminder item generation.

System-wide parameters (closing day, global start date, auto-reminder toggle
and lead days) are persisted in ``system_settings`` with an append-only
``system_setting_changes`` trail. Reminder pending items are derived from real
backend data (per-sheet reviews, bank accounts, canonical transfer decisions)
rather than hard-coded rosters.
"""

from __future__ import annotations

import re
import sqlite3
from datetime import datetime, timezone

from .db import utc_now

# Defaults are applied when a key is absent; the value type is always string.
DEFAULT_SETTINGS: dict[str, str] = {
    "closing_day": "5",
    "start_date": "2026-01-01",
    "auto_remind": "1",
    "remind_days": "3",
}

_SETTING_KEYS = frozenset({"closing_day", "start_date", "auto_remind", "remind_days"})

_DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")


def _valid_date(value: str) -> bool:
    if not _DATE_RE.match(value):
        return False
    try:
        datetime.strptime(value, "%Y-%m-%d")
    except ValueError:
        return False
    return True


def get_settings(connection: sqlite3.Connection) -> dict[str, str]:
    settings = dict(DEFAULT_SETTINGS)
    rows = connection.execute(
        "SELECT key, value FROM system_settings"
    ).fetchall()
    for row in rows:
        settings[row["key"]] = row["value"]
    return settings


def validate_settings(values: dict[str, object]) -> tuple[dict[str, str], str | None]:
    """Return ``(cleaned, error)``; ``cleaned`` holds only recognized keys."""
    cleaned: dict[str, str] = {}
    if "closing_day" in values:
        raw = str(values["closing_day"]).strip()
        try:
            day = int(raw)
        except ValueError:
            return cleaned, "每月结账日须为 1-28 之间的整数。"
        if day < 1 or day > 28:
            return cleaned, "每月结账日须为 1-28 之间的整数。"
        cleaned["closing_day"] = str(day)
    if "start_date" in values:
        raw = str(values["start_date"]).strip()
        if not _valid_date(raw):
            return cleaned, "全局起算日须为有效日期（YYYY-MM-DD）。"
        cleaned["start_date"] = raw
    if "auto_remind" in values:
        raw = str(values["auto_remind"]).strip()
        if raw not in {"0", "1"}:
            return cleaned, "自动提醒开关须为 0 或 1。"
        cleaned["auto_remind"] = raw
    if "remind_days" in values:
        raw = str(values["remind_days"]).strip()
        try:
            days = int(raw)
        except ValueError:
            return cleaned, "提前提醒天数须为不小于 1 的整数。"
        if days < 1 or days > 30:
            return cleaned, "提前提醒天数须为 1-30 之间的整数。"
        cleaned["remind_days"] = str(days)
    return cleaned, None


def update_settings(
    connection: sqlite3.Connection,
    values: dict[str, object],
    actor: sqlite3.Row,
) -> dict[str, str]:
    cleaned, error = validate_settings(values)
    if error is not None:
        raise ValueError(error)
    if not cleaned:
        raise ValueError("没有需要保存的设置项。")
    current = get_settings(connection)
    with connection:
        for key, new_value in cleaned.items():
            old_value = current.get(key)
            if old_value == new_value:
                continue
            connection.execute(
                """
                INSERT INTO system_settings (key, value, updated_by, updated_at)
                VALUES (?, ?, ?, ?)
                ON CONFLICT(key) DO UPDATE SET
                    value = excluded.value,
                    updated_by = excluded.updated_by,
                    updated_at = excluded.updated_at
                """,
                (key, new_value, actor["id"], utc_now()),
            )
            connection.execute(
                """
                INSERT INTO system_setting_changes (
                    key, before_value, after_value,
                    actor_user_id, actor_username, created_at
                ) VALUES (?, ?, ?, ?, ?, ?)
                """,
                (
                    key,
                    old_value,
                    new_value,
                    actor["id"],
                    actor["username"],
                    utc_now(),
                ),
            )
    return get_settings(connection)


# ----------------------------------------------------------------------
# Reminder pending items
# ----------------------------------------------------------------------

def _current_period() -> tuple[str, str]:
    """Return the current calendar month as ``(start, end_exclusive)`` dates."""
    today = datetime.now(timezone.utc)
    start = today.strftime("%Y-%m-01")
    year, month = today.year, today.month
    if month == 12:
        end = f"{year + 1}-01-01"
    else:
        end = f"{year}-{month + 1:02d}-01"
    return start, end


def pending_items(connection: sqlite3.Connection, company_id: int) -> list[dict[str, str]]:
    """Derive the list of pending reminder items for one company."""
    items: list[dict[str, str]] = []

    company = connection.execute(
        "SELECT id, name FROM companies WHERE id = ?", (company_id,)
    ).fetchone()
    if company is None:
        return items

    period_start, _ = _current_period()

    # 1) 本月流水未提交：有已启用账户，但本月没有任何已确认的工作表。
    active_accounts = connection.execute(
        """
        SELECT id, account_number FROM bank_accounts
        WHERE company_id = ? AND status = 'active'
        """,
        (company_id,),
    ).fetchall()
    confirmed_this_period = connection.execute(
        """
        SELECT COUNT(*) AS n
        FROM sheet_reviews rv
        JOIN import_batches b ON b.id = rv.import_batch_id
        WHERE b.company_id = ? AND rv.review_status = 'confirmed'
          AND rv.sheet_batch_id IN (
              SELECT id FROM sheet_batches s
              WHERE s.period_end >= ?
          )
        """,
        (company_id, period_start),
    ).fetchone()["n"]
    if active_accounts and confirmed_this_period == 0:
        items.append(
            {
                "kind": "流水未提交",
                "content": "本月各银行账户流水尚未提交，请尽快上传本月银行流水。",
            }
        )

    # 2) 待确认工作表：仍有未确认的解析结果。
    pending_sheets = connection.execute(
        """
        SELECT COUNT(*) AS n
        FROM sheet_reviews rv
        JOIN import_batches b ON b.id = rv.import_batch_id
        WHERE b.company_id = ? AND rv.review_status = 'pending'
        """,
        (company_id,),
    ).fetchone()["n"]
    if pending_sheets:
        items.append(
            {
                "kind": "待确认工作表",
                "content": f"有 {pending_sheets} 个导入工作表尚未确认，请核对后确认。",
            }
        )

    # 3) 待审核账户登记：处于待复核状态的银行账户。
    pending_accounts = connection.execute(
        """
        SELECT COUNT(*) AS n FROM bank_accounts
        WHERE company_id = ? AND status = 'pending'
        """,
        (company_id,),
    ).fetchone()["n"]
    if pending_accounts:
        items.append(
            {
                "kind": "账户登记",
                "content": f"有 {pending_accounts} 个银行账户登记待审核。",
            }
        )

    # 4) 待确认往来事项：尚未解决的往来匹配。
    pending_transfers = connection.execute(
        """
        SELECT COUNT(*) AS n
        FROM current_transfer_decisions c
        JOIN transfer_match_decisions d ON d.id = c.decision_id
        JOIN transfer_decision_participants p
             ON p.decision_id = d.id AND p.company_id = ?
        WHERE d.classification IN ('unresolved', 'needs_review')
        """,
        (company_id,),
    ).fetchone()["n"]
    if pending_transfers:
        items.append(
            {
                "kind": "往来待确认",
                "content": f"有 {pending_transfers} 项往来流水待确认，请核对对方银行流水佐证。",
            }
        )

    return items


def send_reminders(
    connection: sqlite3.Connection,
    company_id: int,
    actor: sqlite3.Row,
) -> tuple[list[dict[str, str]], str | None]:
    """Create reminder rows for a company's pending items.

    Returns ``(created, deadline)``. ``created`` is the list of persisted
    reminder payloads; ``deadline`` is derived from the closing-day setting.
    """
    items = pending_items(connection, company_id)
    if not items:
        return [], None

    settings = get_settings(connection)
    try:
        closing_day = int(settings["closing_day"])
    except ValueError:
        closing_day = 5
    today = datetime.now(timezone.utc)
    # Deadline: next month's closing day (current month if today is before it).
    if today.day < closing_day:
        deadline = today.strftime(f"%Y-%m-{closing_day:02d}")
    else:
        year, month = today.year, today.month
        if month == 12:
            year, month = year + 1, 1
        else:
            month += 1
        deadline = f"{year}-{month:02d}-{closing_day:02d}"

    created: list[dict[str, str]] = []
    with connection:
        for item in items:
            cursor = connection.execute(
                """
                INSERT INTO reminders (
                    company_id, kind, content, deadline, source,
                    actor_user_id, actor_username, created_at
                ) VALUES (?, ?, ?, ?, 'manual', ?, ?, ?)
                """,
                (
                    company_id,
                    item["kind"],
                    item["content"],
                    deadline,
                    actor["id"],
                    actor["username"],
                    utc_now(),
                ),
            )
            created.append(
                {
                    "id": cursor.lastrowid,
                    "company_id": company_id,
                    "kind": item["kind"],
                    "content": item["content"],
                    "deadline": deadline,
                    "source": "manual",
                    "status": "unread",
                    "actor_username": actor["username"],
                    "created_at": utc_now(),
                }
            )
    return created, deadline
