"""admin 提现:列表(读)+ 单笔/批量查单与审核操作(写,带审计)。

提现的钱逻辑(查微信/退款/撤单/幂等)全部复用 app.repositories.wallet,admin 只触发 + 记审计。
这些 wallet 函数内部各自 commit(涉及微信调用),审计在其后单独 commit:操作本身幂等,
审计记录"谁触发的 + 结果",事务边界比"改金币"宽松是有意的(不能塞进 wallet 的自有事务)。
"""
from __future__ import annotations

from datetime import datetime
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Query, Request

from app.admin.audit import write_audit
from app.admin.deps import AdminDb, get_client_ip, get_current_admin, require_role
from app.admin.repositories import queries
from app.admin.schemas.admin import AdminAuditLogOut
from app.admin.schemas.common import CursorPage
from app.admin.schemas.wallet import (
    CashTxnOut,
    InviteOverviewOut,
    WithdrawBulkItemResult,
    WithdrawBulkRejectRequest,
    WithdrawBulkRequest,
    WithdrawBulkResult,
    WithdrawDetailOut,
    WithdrawListItemOut,
    WithdrawOrderOut,
    WithdrawRejectRequest,
    WithdrawSummaryOut,
    WithdrawUserSnapshot,
    WxpayHealthCheckOut,
)
from app.core.config import settings
from app.integrations import wxpay
from app.models.admin import AdminUser
from app.repositories import app_config
from app.repositories import wallet as wallet_repo

router = APIRouter(
    prefix="/admin/api/withdraws",
    tags=["admin-withdraw"],
    dependencies=[Depends(get_current_admin)],
)


@router.get("", response_model=CursorPage[WithdrawListItemOut], summary="提现单列表")
def list_withdraws(
    db: AdminDb,
    user_id: Annotated[int | None, Query()] = None,
    status: Annotated[str | None, Query()] = None,
    # 提现类型筛选:coin_cash(福利页提现)/ invite_cash(邀请提现);None=全部
    source: Annotated[str | None, Query(pattern="^(coin_cash|invite_cash)$")] = None,
    keyword: Annotated[str | None, Query(max_length=100)] = None,
    date_from: Annotated[datetime | None, Query()] = None,
    date_to: Annotated[datetime | None, Query()] = None,
    date_field: Annotated[str, Query(pattern="^(created_at|updated_at)$")] = "created_at",
    sort_by: Annotated[
        str, Query(pattern="^(id|created_at|updated_at|amount_cents)$")
    ] = "created_at",
    sort_order: Annotated[str, Query(pattern="^(asc|desc)$")] = "desc",
    quick_filter: Annotated[
        str | None,
        Query(pattern="^(abnormal|failed|overdue_reviewing|pending_overdue|today|high_risk)$"),
    ] = None,
    limit: Annotated[int, Query(ge=1, le=100)] = 20,
    cursor: Annotated[int | None, Query()] = None,
) -> CursorPage[WithdrawListItemOut]:
    items, next_cursor, total = queries.list_all_withdraw_orders(
        db,
        user_id=user_id,
        status=status,
        source=source,
        keyword=keyword,
        date_from=date_from,
        date_to=date_to,
        date_field=date_field,
        sort_by=sort_by,
        sort_order=sort_order,
        quick_filter=quick_filter,
        limit=limit,
        cursor=cursor,
    )
    # 联表带出手机号/昵称 + 累计成功提现(本页 user_id 批量富化,2 条聚合查询,无 N+1)。
    enrichment = queries.withdraw_list_enrichment(
        db, [o.user_id for o in items], source=source
    )
    _empty = {
        "phone": None,
        "nickname": None,
        "is_high_risk": False,
        "high_risk_note": None,
        "cumulative_success_cents": 0,
    }
    out_items = [
        WithdrawListItemOut.model_validate(o).model_copy(
            update=enrichment.get(o.user_id, _empty)
        )
        for o in items
    ]
    return CursorPage(items=out_items, next_cursor=next_cursor, total=total)


@router.get("/summary", response_model=WithdrawSummaryOut, summary="提现审核台统计")
def withdraws_summary(
    db: AdminDb,
    source: Annotated[
        str | None, Query(pattern="^(coin_cash|invite_cash)$")
    ] = None,
) -> WithdrawSummaryOut:
    return WithdrawSummaryOut(**queries.withdraw_summary(db, source=source))


@router.get(
    "/health-check",
    response_model=WxpayHealthCheckOut,
    summary="提现配置健康检查",
    dependencies=[Depends(require_role("finance"))],  # 暴露密钥路径/配置,限财务+super
)
def withdraw_health_check(db: AdminDb) -> WxpayHealthCheckOut:
    private_path = wxpay._resolve_config_path(settings.WXPAY_MCH_PRIVATE_KEY_PATH)  # noqa: SLF001
    public_path = wxpay._resolve_config_path(settings.WXPAY_PUBLIC_KEY_PATH)  # noqa: SLF001
    issues: list[str] = []

    private_loadable = False
    public_loadable = False
    try:
        wxpay._load_private_key()  # noqa: SLF001
        private_loadable = True
    except Exception as e:  # noqa: BLE001
        issues.append(f"商户私钥不可用:{e}")
    try:
        wxpay._load_public_key()  # noqa: SLF001
        public_loadable = True
    except Exception as e:  # noqa: BLE001
        issues.append(f"微信支付平台公钥不可用:{e}")

    if not settings.wxpay_configured:
        issues.append("微信支付基础配置不完整")
    if not settings.WXPAY_AUTH_NOTIFY_URL:
        issues.append("免确认授权回调地址未配置")

    # 实际是否自动对账 = env 部署总闸(worker 起没起)AND 运营后台 DB 开关(本轮跑不跑)。
    # 自动查单属于非阻断运维能力:状态继续返回给调用方,但关闭时不计入微信提现配置 issues,
    # 避免把“没有自动扫单”误报成“无法打款”。
    worker_running = settings.WITHDRAW_AUTO_RECONCILE_ENABLED
    daily_on = bool(app_config.get_value(db, "withdraw_auto_reconcile_enabled"))
    auto_reconcile_enabled = worker_running and daily_on

    return WxpayHealthCheckOut(
        ok=not issues,
        wxpay_configured=settings.wxpay_configured,
        wxpay_auth_configured=settings.wxpay_auth_configured,
        private_key_path=str(private_path),
        private_key_exists=private_path.exists(),
        private_key_loadable=private_loadable,
        public_key_path=str(public_path),
        public_key_exists=public_path.exists(),
        public_key_loadable=public_loadable,
        auth_notify_url_configured=bool(settings.WXPAY_AUTH_NOTIFY_URL),
        auto_reconcile_enabled=auto_reconcile_enabled,
        auto_reconcile_interval_sec=settings.WITHDRAW_AUTO_RECONCILE_INTERVAL_SEC,
        auto_reconcile_older_than_minutes=settings.WITHDRAW_AUTO_RECONCILE_OLDER_THAN_MINUTES,
        issues=issues,
    )


@router.get("/{out_bill_no}", response_model=WithdrawDetailOut, summary="提现单详情")
def withdraw_detail(
    out_bill_no: str,
    db: AdminDb,
    date_from: Annotated[datetime | None, Query()] = None,
    date_to: Annotated[datetime | None, Query()] = None,
) -> WithdrawDetailOut:
    order = queries.get_withdraw_by_out_bill_no(db, out_bill_no)
    if order is None:
        raise HTTPException(status_code=404, detail="提现单不存在")

    overview = queries.get_user_overview(db, order.user_id)
    user_snapshot = None
    if overview is not None:
        user = overview["user"]
        user_snapshot = WithdrawUserSnapshot(
            id=user.id,
            phone=user.phone,
            nickname=user.nickname,
            is_high_risk=user.is_high_risk,
            high_risk_note=user.high_risk_note,
            status=user.status,
            wechat_nickname=user.wechat_nickname,
            wechat_avatar_url=user.wechat_avatar_url,
            created_at=user.created_at,
            last_login_at=user.last_login_at,
            cash_balance_cents=overview["cash_balance_cents"],
            withdraw_total=overview["withdraw_total"],
            withdraw_success_cents=overview["withdraw_success_cents"],
        )

    recent_withdraws, _, _ = queries.list_all_withdraw_orders(
        db, user_id=order.user_id, limit=5, cursor=None,
    )
    recent_cash_transactions, _ = queries.list_all_cash_transactions(
        db, user_id=order.user_id, limit=8, cursor=None,
    )
    audit_logs = queries.list_withdraw_audit_logs(db, out_bill_no, limit=10)
    risk_flags, risk_score = queries.withdraw_risk_flags(
        order,
        overview["user"] if overview else None,
        recent_withdraws,
        overview["cash_balance_cents"] if overview else 0,
    )
    detail_enrichment = queries.withdraw_list_enrichment(
        db, [order.user_id], source=order.source
    ).get(order.user_id, {})

    return WithdrawDetailOut(
        order=WithdrawOrderOut.model_validate(order),
        user=user_snapshot,
        cumulative_success_cents=int(
            detail_enrichment.get("cumulative_success_cents", 0)
        ),
        risk_flags=risk_flags,
        risk_score=risk_score,
        recent_withdraws=[WithdrawOrderOut.model_validate(o) for o in recent_withdraws],
        recent_cash_transactions=[CashTxnOut.model_validate(t) for t in recent_cash_transactions],
        audit_logs=[AdminAuditLogOut.model_validate(log) for log in audit_logs],
        invite_overview=(
            InviteOverviewOut(
                **queries.invite_overview(
                    db,
                    order.user_id,
                    date_from=date_from,
                    date_to=date_to,
                )
            )
            if order.source == "invite_cash"
            else None
        ),
    )


def _bulk_result(items: list[WithdrawBulkItemResult]) -> WithdrawBulkResult:
    success = sum(1 for item in items if item.ok)
    return WithdrawBulkResult(
        total=len(items),
        success=success,
        failed=len(items) - success,
        items=items,
    )


@router.post("/bulk/refresh", response_model=WithdrawBulkResult, summary="批量刷新查单")
def bulk_refresh_withdraws(
    body: WithdrawBulkRequest,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawBulkResult:
    results: list[WithdrawBulkItemResult] = []
    ip = get_client_ip(request)
    for out_bill_no in body.out_bill_nos:
        try:
            order = queries.get_withdraw_by_out_bill_no(db, out_bill_no)
            if order is None:
                raise wallet_repo.WithdrawOrderNotFound
            refreshed = wallet_repo.refresh_withdraw_status(
                db, order.user_id, out_bill_no, cancel_if_unconfirmed=True,
            )
            write_audit(
                db, admin, action="withdraw.refresh", target_type="withdraw",
                target_id=out_bill_no,
                detail={
                    "status": refreshed.status,
                    "wechat_state": refreshed.wechat_state,
                    "fail_reason": refreshed.fail_reason,
                    "bulk": True,
                },
                ip=ip, commit=True,
            )
            results.append(
                WithdrawBulkItemResult(
                    out_bill_no=out_bill_no, ok=True, status=refreshed.status
                )
            )
        except wallet_repo.WithdrawOrderNotFound:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error="提现单不存在")
            )
        except wxpay.WxPayNotConfiguredError:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error="微信支付未配置")
            )
        except Exception as e:  # noqa: BLE001 - 批量操作单笔失败不打断整批
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error=f"系统异常: {e}")
            )
    return _bulk_result(results)


@router.post("/bulk/approve", response_model=WithdrawBulkResult, summary="批量审核通过并打款")
def bulk_approve_withdraws(
    body: WithdrawBulkRequest,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawBulkResult:
    results: list[WithdrawBulkItemResult] = []
    ip = get_client_ip(request)
    for out_bill_no in body.out_bill_nos:
        try:
            order = wallet_repo.approve_withdraw(db, out_bill_no)
            write_audit(
                db, admin, action="withdraw.approve", target_type="withdraw",
                target_id=out_bill_no,
                detail={
                    "status": order.status,
                    "wechat_state": order.wechat_state,
                    "amount_cents": order.amount_cents,
                    "bulk": True,
                },
                ip=ip, commit=True,
            )
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=True, status=order.status)
            )
        except wallet_repo.WithdrawOrderNotFound:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error="提现单不存在")
            )
        except wallet_repo.WithdrawNotReviewable as e:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error=str(e))
            )
        except wxpay.WxPayNotConfiguredError:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error="微信支付未配置")
            )
        except Exception as e:  # noqa: BLE001 - 批量操作单笔失败不打断整批
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error=f"系统异常: {e}")
            )
    return _bulk_result(results)


@router.post("/bulk/reject", response_model=WithdrawBulkResult, summary="批量审核拒绝并退款")
def bulk_reject_withdraws(
    body: WithdrawBulkRejectRequest,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawBulkResult:
    results: list[WithdrawBulkItemResult] = []
    ip = get_client_ip(request)
    for out_bill_no in body.out_bill_nos:
        try:
            order = wallet_repo.reject_withdraw(db, out_bill_no, body.reason)
            write_audit(
                db, admin, action="withdraw.reject", target_type="withdraw",
                target_id=out_bill_no,
                detail={
                    "reason": body.reason,
                    "amount_cents": order.amount_cents,
                    "bulk": True,
                },
                ip=ip, commit=True,
            )
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=True, status=order.status)
            )
        except wallet_repo.WithdrawOrderNotFound:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error="提现单不存在")
            )
        except wallet_repo.WithdrawNotReviewable as e:
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error=str(e))
            )
        except Exception as e:  # noqa: BLE001 - 批量操作单笔失败不打断整批
            db.rollback()
            results.append(
                WithdrawBulkItemResult(out_bill_no=out_bill_no, ok=False, error=f"系统异常: {e}")
            )
    return _bulk_result(results)


@router.post("/{out_bill_no}/refresh", response_model=WithdrawOrderOut, summary="单笔重试查单")
def refresh_withdraw(
    out_bill_no: str,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawOrderOut:
    order = queries.get_withdraw_by_out_bill_no(db, out_bill_no)
    if order is None:
        raise HTTPException(status_code=404, detail="提现单不存在")
    try:
        refreshed = wallet_repo.refresh_withdraw_status(
            db, order.user_id, out_bill_no, cancel_if_unconfirmed=True,
        )
    except wxpay.WxPayNotConfiguredError as e:
        raise HTTPException(status_code=503, detail="微信支付未配置") from e
    write_audit(
        db, admin, action="withdraw.refresh", target_type="withdraw", target_id=out_bill_no,
        detail={
            "status": refreshed.status,
            "wechat_state": refreshed.wechat_state,
            "fail_reason": refreshed.fail_reason,
        },
        ip=get_client_ip(request), commit=True,
    )
    return WithdrawOrderOut.model_validate(refreshed)


@router.post("/{out_bill_no}/approve", response_model=WithdrawOrderOut, summary="审核通过(发起微信打款)")
def approve_withdraw_order(
    out_bill_no: str,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawOrderOut:
    """审核通过 → 调微信转账。仅 reviewing 单可通过(非 reviewing 返 409,防重复打款)。

    打款的钱逻辑(转账/查单/退款/幂等)复用 wallet_repo.approve_withdraw → execute_withdraw_transfer;
    返回的 order 可能是 pending(在途/待用户确认)/success/failed(打款失败已退),admin 前端按 status 展示。
    """
    try:
        order = wallet_repo.approve_withdraw(db, out_bill_no)
    except wallet_repo.WithdrawOrderNotFound as e:
        raise HTTPException(status_code=404, detail="提现单不存在") from e
    except wallet_repo.WithdrawNotReviewable as e:
        raise HTTPException(status_code=409, detail=str(e)) from e
    except wxpay.WxPayNotConfiguredError as e:
        raise HTTPException(status_code=503, detail="微信支付未配置") from e
    write_audit(
        db, admin, action="withdraw.approve", target_type="withdraw", target_id=out_bill_no,
        detail={
            "status": order.status,
            "wechat_state": order.wechat_state,
            "amount_cents": order.amount_cents,
        },
        ip=get_client_ip(request), commit=True,
    )
    return WithdrawOrderOut.model_validate(order)


@router.post("/{out_bill_no}/reject", response_model=WithdrawOrderOut, summary="审核拒绝(退回现金)")
def reject_withdraw_order(
    out_bill_no: str,
    body: WithdrawRejectRequest,
    request: Request,
    admin: Annotated[AdminUser, Depends(require_role("finance"))],
    db: AdminDb,
) -> WithdrawOrderOut:
    """审核拒绝 → 退回用户现金 + 置 rejected,理由写入 fail_reason(用户可见)。仅 reviewing 单可拒。"""
    try:
        order = wallet_repo.reject_withdraw(db, out_bill_no, body.reason)
    except wallet_repo.WithdrawOrderNotFound as e:
        raise HTTPException(status_code=404, detail="提现单不存在") from e
    except wallet_repo.WithdrawNotReviewable as e:
        raise HTTPException(status_code=409, detail=str(e)) from e
    write_audit(
        db, admin, action="withdraw.reject", target_type="withdraw", target_id=out_bill_no,
        detail={"reason": body.reason, "amount_cents": order.amount_cents},
        ip=get_client_ip(request), commit=True,
    )
    return WithdrawOrderOut.model_validate(order)
