"""为本地“风控监控”页面灌入一组可重复的完整演示数据。

仅允许在 APP_ENV=dev 运行。脚本会重建 ``risk-demo-*`` 前缀的数据，并创建本地
后台账号 ``risk_demo / RiskDemo123!``，方便端到端和视觉验收。
"""
from __future__ import annotations

from datetime import timedelta

from sqlalchemy import delete, select

from app.admin.repositories import admin_user as admin_repo
from app.core.config import settings
from app.core.security import hash_password
from app.db.session import SessionLocal
from app.models.comparison import ComparisonRecord
from app.models.risk import BehaviorEvent, RiskIncident, SubjectRestriction
from app.repositories import risk as risk_repo
from app.repositories import user as user_repo

DEMO_PREFIX = "risk-demo-"
ADMIN_USERNAME = "risk_demo"
ADMIN_PASSWORD = "RiskDemo123!"


def _reset_demo(db) -> None:
    demo_user_ids = list(
        db.scalars(
            select(ComparisonRecord.user_id)
            .where(
                ComparisonRecord.trace_id.like(f"{DEMO_PREFIX}%"),
                ComparisonRecord.user_id.is_not(None),
            )
            .distinct()
        ).all()
    )
    demo_incident_ids = list(
        db.scalars(
            select(RiskIncident.id).where(
                (RiskIncident.subject_id.like(f"{DEMO_PREFIX}%"))
                | (
                    (RiskIncident.rule_code == risk_repo.RULE_COMPARE_DAILY)
                    & (RiskIncident.subject_id.in_([str(value) for value in demo_user_ids]))
                )
            )
        ).all()
    )
    if demo_incident_ids:
        db.execute(
            delete(SubjectRestriction).where(
                SubjectRestriction.incident_id.in_(demo_incident_ids)
            )
        )
    db.execute(
        delete(SubjectRestriction).where(
            SubjectRestriction.subject_id.like(f"{DEMO_PREFIX}%")
        )
    )
    db.execute(
        delete(RiskIncident).where(
            (RiskIncident.subject_id.like(f"{DEMO_PREFIX}%"))
            | (
                (RiskIncident.rule_code == risk_repo.RULE_COMPARE_DAILY)
                & (RiskIncident.subject_id.in_([str(value) for value in demo_user_ids]))
            )
        )
    )
    db.execute(
        delete(BehaviorEvent).where(
            BehaviorEvent.subject_id.like(f"{DEMO_PREFIX}%")
        )
    )
    db.execute(
        delete(ComparisonRecord).where(
            ComparisonRecord.trace_id.like(f"{DEMO_PREFIX}%")
        )
    )
    db.commit()


def _ensure_admin(db) -> None:
    admin = admin_repo.get_by_username(db, ADMIN_USERNAME)
    if admin is None:
        admin_repo.create_admin(
            db,
            username=ADMIN_USERNAME,
            password=ADMIN_PASSWORD,
            role="super_admin",
        )
        return
    admin.password_hash = hash_password(ADMIN_PASSWORD)
    admin.role = "super_admin"
    admin.status = "active"
    db.commit()


def _seed_sms(db, now) -> None:
    alerted = [
        ("risk-demo-sms-oppo-a5", "OPPO A5", "13812343001", 9),
        ("risk-demo-sms-xiaomi14", "Xiaomi 14", "13812343002", 7),
        ("risk-demo-sms-iphone15", "iPhone 15", "13812343003", 5),
    ]
    events: list[BehaviorEvent] = []
    for device_id, model, phone, count in alerted:
        for index in range(count):
            events.append(
                BehaviorEvent(
                    event_type=risk_repo.EVENT_SMS_SEND,
                    subject_type="device",
                    subject_id=device_id,
                    device_id=device_id,
                    device_model=model,
                    phone=phone if index == 0 else f"138{index:08d}"[-11:],
                    client_ip="127.0.0.1",
                    outcome="success",
                    details={"demo": True},
                    occurred_at=now + timedelta(seconds=index),
                )
            )
    # 其余成功下发分散到未达阈值的设备，每台最多 4 条，确保报警设备仍严格为 3 台。
    remaining = 1284 - sum(row[3] for row in alerted)
    for index in range(remaining):
        device_no = index // 4
        device_id = f"risk-demo-sms-normal-{device_no:04d}"
        events.append(
            BehaviorEvent(
                event_type=risk_repo.EVENT_SMS_SEND,
                subject_type="device",
                subject_id=device_id,
                device_id=device_id,
                device_model="演示普通设备",
                phone=f"137{index % 100_000_000:08d}",
                client_ip="127.0.0.1",
                outcome="success",
                details={"demo": True},
                occurred_at=now + timedelta(seconds=index % 1800),
            )
        )
    db.add_all(events)
    db.flush()
    for device_id, _, phone, _ in alerted:
        risk_repo.evaluate_behavior_rule(
            db,
            rule_code=risk_repo.RULE_SMS_HOURLY,
            subject_id=device_id,
            at=now,
        )
        user = user_repo.upsert_user_for_login(
            db, phone=phone, register_channel="sms"
        )
        db.add(
            BehaviorEvent(
                event_type=risk_repo.EVENT_SMS_LOGIN,
                subject_type="device",
                subject_id=device_id,
                user_id=user.id,
                device_id=device_id,
                phone=phone,
                outcome="success",
                details={"demo": True},
                occurred_at=now + timedelta(minutes=2),
            )
        )
    db.commit()


def _seed_oneclick(db, now) -> None:
    devices = [
        ("risk-demo-oneclick-oppo-reno", "OPPO Reno", "13912343001", 482),
        ("risk-demo-oneclick-vivo-y36", "vivo Y36", "13912343002", 481),
    ]
    for device_id, model, phone, count in devices:
        user = user_repo.upsert_user_for_login(
            db, phone=phone, register_channel="jverify"
        )
        db.add_all(
            [
                BehaviorEvent(
                    event_type=risk_repo.EVENT_ONECLICK_LOGIN,
                    subject_type="device",
                    subject_id=device_id,
                    user_id=user.id,
                    device_id=device_id,
                    device_model=model,
                    phone=phone,
                    client_ip="127.0.0.1",
                    outcome="failed" if index % 11 == 0 else "success",
                    reason="运营商校验失败" if index % 11 == 0 else None,
                    details={"demo": True},
                    occurred_at=now + timedelta(seconds=index * 2),
                )
                for index in range(count)
            ]
        )
        db.flush()
        risk_repo.evaluate_behavior_rule(
            db,
            rule_code=risk_repo.RULE_ONECLICK_DAILY,
            subject_id=device_id,
            at=now,
        )
    db.commit()


def _seed_compare(db, now) -> None:
    local_now = now.astimezone(risk_repo.CN_TZ).replace(tzinfo=None)
    accounts = [
        ("13612343281", "risk-demo-compare-device-a", 1053),
        ("13511106208", "risk-demo-compare-device-b", 1052),
    ]
    for phone, device_id, count in accounts:
        user = user_repo.upsert_user_for_login(
            db, phone=phone, register_channel="sms"
        )
        rows = []
        for index in range(count):
            rows.append(
                ComparisonRecord(
                    user_id=user.id,
                    device_id=device_id,
                    trace_id=f"{DEMO_PREFIX}compare-{user.id}-{index}",
                    store_name=("春熙路小吃店", "科技园轻食", "万达广场烤肉")[
                        index % 3
                    ],
                    product_names=("招牌套餐", "鸡胸沙拉", "双人烤肉")[index % 3],
                    status="failed" if index % 17 == 0 else "success",
                    information="演示比价记录",
                    items=[{"name": "演示菜品", "qty": 1}],
                    comparison_results=[
                        {
                            "platform_id": "meituan",
                            "platform_name": "美团",
                            "price": 23.8,
                        },
                        {
                            "platform_id": "taobao",
                            "platform_name": "淘宝",
                            "price": 21.5,
                        },
                        {
                            "platform_id": "jd",
                            "platform_name": "京东",
                            "price": 22.2,
                        },
                    ],
                    skipped_dish_names=[],
                    saved_amount_cents=230,
                    raw_payload={"demo": True},
                    created_at=local_now + timedelta(seconds=index * 2),
                )
            )
        db.add_all(rows)
        db.commit()
        risk_repo.sync_compare_incident(db, user_id=user.id, at=local_now)


def main() -> None:
    if settings.APP_ENV != "dev":
        raise SystemExit("拒绝执行：风控演示数据脚本仅允许 APP_ENV=dev")
    now = risk_repo.utcnow().replace(minute=10, second=0, microsecond=0)
    with SessionLocal() as db:
        _reset_demo(db)
        _ensure_admin(db)
        _seed_sms(db, now)
        _seed_oneclick(db, now)
        _seed_compare(db, now)
    print("风控监控演示数据已重建")
    print(f"后台账号：{ADMIN_USERNAME}")
    print(f"后台密码：{ADMIN_PASSWORD}")
    print("期望卡片：短信 3 / 1284；一键登录 2 / 963；比价 2 / 2105")


if __name__ == "__main__":
    main()
