"""FEAT-010 数据生命周期（注销 / 恢复 / 导出 / 30 天后硬删）。

对应 docs/legal/data-lifecycle.md。

核心流程：
1. `unregister(user_id)` → 软删（`users.deleted_at = now`）。用户此后不能登录 / 发信 /
   被搜到，但数据保留 30 天"冷静期"。
2. 30 天内用户想恢复 → 邮件 yarnb@qq.com → admin 跑 `reactivate(user_id)`。
3. `purge_expired(older_than_days=30)` 每天 cron 扫 → 对过期账号硬删
   profiles/photos/messages/blocks/reports/sessions/notify_state/envelope_usage/envelope_balance，
   **保留** users 行但 scrub phone_hash 成 `'DELETED:' || id`（保唯一约束防复注），
   **保留** envelope_orders（5 年 会计法审计留存）。
4. `hard_delete(user_id)` → admin 通道立即硬删（GDPR §17 "被遗忘权"硬性要求）。
5. `export_data(user_id) -> dict` → 全数据导出（PIPL §45 可携带权）。
"""

from __future__ import annotations

import json
from typing import Any

from .db import get_conn, now_ts


class AccountError(Exception):
    def __init__(self, code: str, msg: str):
        super().__init__(msg)
        self.code = code


# 冷静期：过了 GRACE_DAYS 天 purge_expired 会把账号硬删
GRACE_DAYS = 30


def unregister(user_id: str) -> dict[str, Any]:
    """软删：标记 users.deleted_at = now。其他表暂不动，等 grace 过后由 purge_expired 清理。

    幂等：对已经 deleted_at != NULL 的用户再调也不抛，返回当前 deleted_at。
    """
    now = now_ts()
    with get_conn() as conn:
        row = conn.execute("SELECT id, deleted_at FROM users WHERE id = ?", (user_id,)).fetchone()
        if row is None:
            raise AccountError("user_not_found", f"user {user_id} 不存在")
        if row["deleted_at"] is not None:
            return {
                "user_id": user_id,
                "deleted_at": row["deleted_at"],
                "grace_until": row["deleted_at"] + GRACE_DAYS * 86400,
                "already_unregistered": True,
            }

        conn.execute("UPDATE users SET deleted_at = ? WHERE id = ?", (now, user_id))
        # 注销立即删除 agent_gateway_url / hooks_token（敏感凭证，不留）
        conn.execute(
            "UPDATE profiles SET agent_gateway_url = NULL, agent_hooks_token = NULL "
            "WHERE user_id = ?",
            (user_id,),
        )
        # 立即失效所有 session（不能继续用 token）
        conn.execute("DELETE FROM sessions WHERE user_id = ?", (user_id,))

    return {
        "user_id": user_id,
        "deleted_at": now,
        "grace_until": now + GRACE_DAYS * 86400,
        "already_unregistered": False,
    }


def reactivate(user_id: str) -> dict[str, Any]:
    """撤销软删（仅 grace 期内有效）。超过 GRACE_DAYS 之后 purge_expired 可能已物理清过数据，
    这时即使 users 行被 scrub 了 phone_hash，resurrect 也无意义。

    admin 专用：普通用户按 doc 要求走邮件流。
    """
    now = now_ts()
    with get_conn() as conn:
        row = conn.execute(
            "SELECT id, deleted_at, phone_hash FROM users WHERE id = ?", (user_id,)
        ).fetchone()
        if row is None:
            raise AccountError("user_not_found", f"user {user_id} 不存在")
        if row["deleted_at"] is None:
            raise AccountError("not_unregistered", f"user {user_id} 未注销，无需恢复")

        # 已过 grace 或已被 scrub（phone_hash 前缀 DELETED:）→ 不能恢复
        if row["phone_hash"].startswith("DELETED:"):
            raise AccountError("already_purged", f"user {user_id} 已过冷静期并清理，无法恢复")
        if now - row["deleted_at"] > GRACE_DAYS * 86400:
            raise AccountError(
                "grace_expired",
                f"user {user_id} 注销已超过 {GRACE_DAYS} 天冷静期，无法恢复",
            )

        conn.execute("UPDATE users SET deleted_at = NULL WHERE id = ?", (user_id,))

    return {"user_id": user_id, "reactivated_at": now}


def export_data(user_id: str) -> dict[str, Any]:
    """导出本人数据为 JSON 可序列化 dict（PIPL §45 可携带权）。

    包含：user 行 / profile / messages（发 + 收） / blocks / reports / envelope_orders /
    envelope_balance / user_photos。不含：phone_hash / cert_id_hash（这些是 hash，对用户无
    意义且暴露会降安全）。
    """
    with get_conn() as conn:
        u = conn.execute(
            "SELECT id, created_at, deleted_at, status, status_reason, status_updated_at "
            "FROM users WHERE id = ?",
            (user_id,),
        ).fetchone()
        if u is None:
            raise AccountError("user_not_found", f"user {user_id} 不存在")

        profile = conn.execute("SELECT * FROM profiles WHERE user_id = ?", (user_id,)).fetchone()
        sent = conn.execute(
            "SELECT id, to_user_id, body, sent_at, delivery_status, read_at, replied_at, "
            "envelope_source, expires_at "
            "FROM messages WHERE from_user_id = ? ORDER BY sent_at DESC",
            (user_id,),
        ).fetchall()
        received = conn.execute(
            "SELECT id, from_user_id, body, sent_at, delivery_status, read_at, replied_at, "
            "envelope_source, expires_at "
            "FROM messages WHERE to_user_id = ? AND deleted_by_recipient_at IS NULL "
            "ORDER BY sent_at DESC",
            (user_id,),
        ).fetchall()
        blocks_out = conn.execute(
            "SELECT blocked_id, created_at FROM blocks WHERE blocker_id = ?", (user_id,)
        ).fetchall()
        blocks_in = conn.execute(
            "SELECT blocker_id, created_at FROM blocks WHERE blocked_id = ?", (user_id,)
        ).fetchall()
        reports_by = conn.execute(
            "SELECT id, message_id, reported_id, reason, created_at, review_status "
            "FROM reports WHERE reporter_id = ?",
            (user_id,),
        ).fetchall()
        orders = conn.execute(
            "SELECT id, count, amount_cents, paid, created_at, provider, status "
            "FROM envelope_orders WHERE user_id = ? ORDER BY created_at DESC",
            (user_id,),
        ).fetchall()
        balance = conn.execute(
            "SELECT paid_remaining, total_purchased FROM envelope_balance WHERE user_id = ?",
            (user_id,),
        ).fetchone()
        photos = conn.execute(
            "SELECT id, purpose, oss_key, sha256, content_type, size_bytes, uploaded_at, "
            "moderation_status FROM user_photos WHERE user_id = ?",
            (user_id,),
        ).fetchall()

    return {
        "schema_version": 1,
        "exported_at": now_ts(),
        "user": dict(u),
        "profile": dict(profile) if profile else None,
        "messages_sent": [dict(r) for r in sent],
        "messages_received": [dict(r) for r in received],
        "blocks_outgoing": [dict(r) for r in blocks_out],
        "blocks_incoming": [dict(r) for r in blocks_in],
        "reports_filed": [dict(r) for r in reports_by],
        "envelope_orders": [dict(r) for r in orders],
        "envelope_balance": dict(balance) if balance else None,
        "photos": [dict(r) for r in photos],
    }


def export_data_json(user_id: str) -> str:
    """export_data 的 JSON 字符串包装（CLI 里 print）。"""
    return json.dumps(export_data(user_id), ensure_ascii=False, indent=2)


def _scrub_and_purge(conn, user_id: str) -> None:
    """内部：对 user_id 执行文档规定的清理策略。需在事务里调。

    - DELETE profiles / photos / messages / blocks / reports / notify_state /
      envelope_usage / envelope_balance / sessions
    - UPDATE users SET phone_hash='DELETED:'||id（保唯一防复注），保留 created_at / deleted_at
    - **不删** envelope_orders（5 年会计法保留）
    """
    conn.execute("DELETE FROM profiles WHERE user_id = ?", (user_id,))
    conn.execute("DELETE FROM user_photos WHERE user_id = ?", (user_id,))
    conn.execute(
        "DELETE FROM messages WHERE from_user_id = ? OR to_user_id = ?",
        (user_id, user_id),
    )
    conn.execute(
        "DELETE FROM blocks WHERE blocker_id = ? OR blocked_id = ?",
        (user_id, user_id),
    )
    conn.execute(
        "DELETE FROM reports WHERE reporter_id = ? OR reported_id = ?",
        (user_id, user_id),
    )
    conn.execute("DELETE FROM notify_state WHERE user_id = ?", (user_id,))
    conn.execute("DELETE FROM envelope_usage WHERE user_id = ?", (user_id,))
    conn.execute("DELETE FROM envelope_balance WHERE user_id = ?", (user_id,))
    conn.execute("DELETE FROM sessions WHERE user_id = ?", (user_id,))
    # scrub users 行（phone_hash 要保唯一；放 'DELETED:' prefix + id 保证不冲突）
    conn.execute(
        "UPDATE users SET phone_hash = 'DELETED:' || id WHERE id = ?",
        (user_id,),
    )


def purge_expired(older_than_days: int = GRACE_DAYS) -> dict[str, Any]:
    """硬删冷静期已过的账号。每天 cron 调。返回详情供审计。"""
    cutoff = now_ts() - older_than_days * 86400
    with get_conn() as conn:
        rows = conn.execute(
            "SELECT id FROM users WHERE deleted_at IS NOT NULL AND deleted_at < ? "
            "AND phone_hash NOT LIKE 'DELETED:%'",
            (cutoff,),
        ).fetchall()
        purged_ids = [r["id"] for r in rows]
        for uid in purged_ids:
            _scrub_and_purge(conn, uid)

    return {
        "purged_count": len(purged_ids),
        "purged_ids": purged_ids,
        "cutoff_ts": cutoff,
    }


def hard_delete(user_id: str) -> dict[str, Any]:
    """立即硬删（GDPR §17 "被遗忘权"）。不走 30 天冷静期。

    admin 专用：调用方需验权。和 purge_expired 用同一套清理逻辑。
    """
    now = now_ts()
    with get_conn() as conn:
        row = conn.execute("SELECT id FROM users WHERE id = ?", (user_id,)).fetchone()
        if row is None:
            raise AccountError("user_not_found", f"user {user_id} 不存在")
        # 先标 deleted_at（如尚未标）以便审计
        conn.execute(
            "UPDATE users SET deleted_at = COALESCE(deleted_at, ?) WHERE id = ?",
            (now, user_id),
        )
        _scrub_and_purge(conn, user_id)

    return {"user_id": user_id, "hard_deleted_at": now}
