"""直接触发「消息通知中心」真实推送链路,给指定用户(默认 11111111111)的已注册设备发 push。

用于**后台无法驱动**的事件联调(本环境:wxpay 未配 → 提现成功打不通、提现单唯一约束 →
造不了多张待审单、好友下单后台无入口)。本脚本直接调 services/notification_events 的真实
下发函数,走的就是生产同一条链路:落 notification 表(站内消息) + 厂商直推(honor/huawei/
xiaomi/oppo/vivo)到该用户 device_liveness 里已注册的 push token。

默认只发这 3 类(后台驱动不了的):
  #3  withdraw_success       提现到账
  #4  withdraw_failed        提现失败,款项已退回
  #12 invite_order_reward    好友下单奖励到账
可用 --types 指定;--types all 追加后台能驱动的 #9/#10/#11(注意:这几类的点击跳转 id 是假的,
仅验证「推送到达手机」,真实跳转请走后台审核流程)。

  .venv\\Scripts\\python.exe scripts\\fire_push_events.py                    # 3 类各 10 条
  .venv\\Scripts\\python.exe scripts\\fire_push_events.py --count 1          # 各 1 条(先小量验证通道)
  .venv\\Scripts\\python.exe scripts\\fire_push_events.py --types withdraw_failed --count 3
  .venv\\Scripts\\python.exe scripts\\fire_push_events.py --types all --count 2

推送成败看输出里的 `shagua.vendor_push` 日志(push sent / push failed);2 台设备则每条各推 2 次。
凭据缺失或 token 失效时 notification_events 只记日志、不抛错(站内消息仍会落库)。
"""
from __future__ import annotations

import argparse
import logging
import random
import sys
import uuid

from app.core.rewards import INVITE_COMPARE_REWARD_CENTS, PRICE_REPORT_REWARD_COINS
from app.db.session import SessionLocal, engine
from app.models.feedback import Feedback
from app.models.price_report import PriceReport
from app.models.wallet import WithdrawOrder
from app.repositories import device as device_repo
from app.repositories import user as user_repo
from app.services import notification_events

# SQL 回显静音;shagua.* 开到 INFO,好看到「push sent / push failed」结果
# dev 下 APP_DEBUG=true → engine echo=True,echo 走 InstanceLogger 直写、无视 logger level,只能关 echo 本身
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)
engine.echo = False

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8")

DEFAULT_PHONE = "11111111111"
DEFAULT_TYPES = ["withdraw_success", "withdraw_failed", "invite_order_reward"]
ADMIN_DRIVEN = ["feedback_reward", "feedback_reply", "report_approved"]  # --types all 追加
ALL_TYPES = DEFAULT_TYPES + ADMIN_DRIVEN

_FAIL_REASONS = [
    "微信零钱未实名,款项已退回",
    "收款账户异常,款项已退回",
    "超出微信零钱收款限额,款项已退回",
]


def _fire_one(db, uid: int, type_key: str, i: int) -> None:
    """构造一条该类型的瞬态业务对象(不落业务表,只为给 notify 函数读字段),触发真实推送。"""
    if type_key == "withdraw_success":
        order = WithdrawOrder(user_id=uid, out_bill_no=uuid.uuid4().hex, amount_cents=50, source="coin_cash")
        notification_events.notify_withdraw_success(db, order)
    elif type_key == "withdraw_failed":
        order = WithdrawOrder(
            user_id=uid, out_bill_no=uuid.uuid4().hex, amount_cents=350, source="coin_cash",
            fail_reason=random.choice(_FAIL_REASONS),
        )
        notification_events.notify_withdraw_failed(db, order)
    elif type_key == "invite_order_reward":
        # 假被邀请人 id(> 真实用户范围,避重):昵称回退「好友」。真实昵称请走 API 流程(见文末说明)。
        fake_invitee = random.randint(900000, 999999)
        notification_events.notify_invite_order_reward(
            db, inviter_user_id=uid, invitee_user_id=fake_invitee, cash_cents=INVITE_COMPARE_REWARD_CENTS
        )
    elif type_key == "feedback_reward":
        fb = Feedback(user_id=uid, content="(直发)", contact="", status="adopted",
                      reward_coins=300, admin_reply="感谢反馈,您说的问题已修复上线,金币请查收～")
        fb.id = random.randint(900000, 999999)
        notification_events.notify_feedback_reward(db, fb)
    elif type_key == "feedback_reply":
        fb = Feedback(user_id=uid, content="(直发)", contact="", status="rejected",
                      admin_reply="您的建议我们记录啦,会在后续版本评估~")
        fb.id = random.randint(900000, 999999)
        notification_events.notify_feedback_reply(db, fb)
    elif type_key == "report_approved":
        rep = PriceReport(
            user_id=uid, reported_platform_id="jd", reported_platform_name="京东外卖",
            reported_price_cents=8800, images=[], status="approved",
            reward_coins=PRICE_REPORT_REWARD_COINS, store_name=f"测试火锅店{i:02d}",
        )
        rep.id = random.randint(900000, 999999)
        notification_events.notify_report_approved(db, rep)
    else:
        raise SystemExit(f"未知类型: {type_key}(可选: {', '.join(ALL_TYPES)})")


def main() -> None:
    parser = argparse.ArgumentParser(description="直接触发消息通知中心真实推送(后台驱动不了的事件用)")
    parser.add_argument("--phone", default=DEFAULT_PHONE, help=f"目标用户手机号(默认 {DEFAULT_PHONE})")
    parser.add_argument("--count", type=int, default=10, help="每类发多少条(默认 10)")
    parser.add_argument(
        "--types", default=",".join(DEFAULT_TYPES),
        help=f"逗号分隔的类型;'all' = {', '.join(ALL_TYPES)}。默认 {', '.join(DEFAULT_TYPES)}",
    )
    args = parser.parse_args()

    types = ALL_TYPES if args.types.strip() == "all" else [t.strip() for t in args.types.split(",") if t.strip()]
    bad = [t for t in types if t not in ALL_TYPES]
    if bad:
        print(f"❌ 未知类型: {', '.join(bad)}(可选: {', '.join(ALL_TYPES)})")
        return

    db = SessionLocal()
    try:
        user = user_repo.get_user_by_phone(db, args.phone)
        if user is None:
            print(f"❌ 用户 {args.phone} 不存在。请先用该手机号在 App 登录一次再跑本脚本。")
            return
        uid = user.id

        targets = device_repo.list_push_targets(db, user_id=uid)
        print(f"目标用户 {args.phone}(id={uid});已注册推送设备 {len(targets)} 台:"
              f"{[t.push_vendor for t in targets] or '无(手机收不到!先在 App 上报 push token)'}")
        print(f"即将触发:{types},每类 {args.count} 条 → 共 {len(types) * args.count} 条\n")

        for t in types:
            print(f"── {t} ×{args.count} " + "─" * 30)
            for i in range(1, args.count + 1):
                _fire_one(db, uid, t, i)

        print(f"\n✅ 已触发完。站内消息已落 notification 表(用 {args.phone} 登录 App 可在消息中心看到);"
              "\n   手机推送成败见上方 `shagua.vendor_push` 日志(push sent=成功 / push failed=失败)。")
    finally:
        db.close()


if __name__ == "__main__":
    main()
