"""Admin 退款退货管理 API

协议：
- 审核通过后：更新订单状态为 refunding，退回积分
- 处理完成：退款到账（标记 completed）
- 拒绝：记录 admin_note + status=rejected
"""
from datetime import datetime
from decimal import Decimal
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_db, get_admin_user, require_permission
from app.core.models.user import User
from app.core.models.customer import Customer
from app.core.models.order import Order, OrderItem
from app.core.models.payment import Payment
from app.core.models.refund import RefundRequest
from app.core.models.member import MemberPointsLedger
from app.core.models.tenant_settings import TenantSettings
from app.core.signals import order_status_changed
from app.core.services import inventory as inv_svc
from app.services.email import build_smtp_config, send_notification

router = APIRouter(prefix="/admin/refund-requests", tags=["Admin 退款管理"])


# ── Schemas ────────────────────────────────────────────────────────────────

class RefundAdminNoteIn(BaseModel):
    admin_note: str


class RefundProcessIn(BaseModel):
    admin_note: Optional[str] = None
    tracking_no: Optional[str] = None
    refund_method: Optional[str] = None  # original / balance / other


class RefundRejectIn(BaseModel):
    admin_note: Optional[str] = None


class RefundOrderSnapshot(BaseModel):
    order_no: str
    status: str
    grand_total: float
    created_at: Optional[str] = None
    paid_at: Optional[str] = None
    customer_name: Optional[str] = None
    customer_phone: Optional[str] = None

    model_config = {"from_attributes": True}


class RefundRequestAdminOut(BaseModel):
    id: int
    order_id: int
    order_no: str
    customer_id: int
    type: str
    status: str
    reason: str
    refund_amount: float
    return_items: Optional[list] = None
    images: Optional[list] = None
    admin_note: Optional[str] = None
    admin_id: Optional[int] = None
    tracking_no: Optional[str] = None
    processed_at: Optional[str] = None
    completed_at: Optional[str] = None
    created_at: Optional[str] = None
    order: Optional[RefundOrderSnapshot] = None
    customer_name: Optional[str] = None

    model_config = {"from_attributes": True}


class RefundListRow(BaseModel):
    id: int
    order_no: str
    customer_id: int
    type: str
    status: str
    refund_amount: float
    created_at: Optional[str] = None
    customer_name: Optional[str] = None

    model_config = {"from_attributes": True}


# ── Admin 退款列表（分页+状态过滤）────────────────────────────────────────

@router.get("", summary="退款申请列表")
async def list_refund_requests(
    status: Optional[str] = Query(None),
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.view")),
):
    q = select(RefundRequest).where(RefundRequest.tenant_id == current_user.tenant_id)
    if status:
        q = q.where(RefundRequest.status == status)
    q = q.order_by(RefundRequest.created_at.desc())

    count_q = select(func.count()).select_from(q.subquery())
    total_r = await db.execute(count_q)
    total = total_r.scalar() or 0

    q = q.offset((page - 1) * page_size).limit(page_size)
    rows_r = await db.execute(q)
    rows = rows_r.scalars().all()

    items = []
    for r in rows:
        # 查顾客名
        cust_r = await db.execute(select(Customer).where(Customer.id == r.customer_id))
        cust = cust_r.scalar_one_or_none()
        items.append(RefundListRow(
            id=r.id,
            order_no=r.order_no,
            customer_id=r.customer_id,
            type=r.type,
            status=r.status,
            refund_amount=float(r.refund_amount),
            created_at=r.created_at.isoformat() if r.created_at else None,
            customer_name=cust.name if cust else None,
        ).model_dump())

    return {"total": total, "page": page, "page_size": page_size, "items": items}


# ── Admin 退款详情 ────────────────────────────────────────────────────────

@router.get("/{refund_id}", summary="退款申请详情")
async def get_refund_request_admin(
    refund_id: int,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.view")),
):
    r = await db.execute(
        select(RefundRequest).where(
            RefundRequest.id == refund_id,
            RefundRequest.tenant_id == current_user.tenant_id,
        )
    )
    rr = r.scalar_one_or_none()
    if not rr:
        raise HTTPException(status_code=404, detail="退款申请不存在")

    return await _format_admin_full(rr, db)


# ── Admin 审核通过 ────────────────────────────────────────────────────────

@router.put("/{refund_id}", summary="审核通过/拒绝（更新状态）")
async def review_refund_request(
    refund_id: int,
    action: str = Query(..., description="approve | reject"),
    body: RefundAdminNoteIn = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.approve")),
):
    r = await db.execute(
        select(RefundRequest).where(
            RefundRequest.id == refund_id,
            RefundRequest.tenant_id == current_user.tenant_id,
        ).with_for_update()
    )
    rr = r.scalar_one_or_none()
    if not rr:
        raise HTTPException(status_code=404, detail="退款申请不存在")

    if rr.status != "pending":
        raise HTTPException(status_code=400, detail=f"当前状态「{rr.status}」不可审核，仅 pending 可操作")

    if action == "approve":
        rr.status = "approved"
        rr.admin_id = current_user.id
        rr.admin_note = (body.admin_note or "") if body else ""
        rr.processed_at = datetime.utcnow()

        # 更新订单状态 → refunding
        order_r = await db.execute(
            select(Order).where(Order.id == rr.order_id).with_for_update()
        )
        order = order_r.scalar_one_or_none()
        if order and order.status not in ("refunding", "refunded"):
            old_status = order.status
            order.status = "refunding"
            order_status_changed.send(
                sender=Order, order=order,
                old_status=old_status, new_status="refunding",
                tenant_id=order.tenant_id,
            )

    elif action == "reject":
        rr.status = "rejected"
        rr.admin_id = current_user.id
        rr.admin_note = (body.admin_note or "") if body else ""
        rr.processed_at = datetime.utcnow()
    else:
        raise HTTPException(status_code=400, detail="action 必须是 approve 或 reject")

    await db.commit()

    # ── 邮件通知 ──────────────────────────────────────────────────
    try:
        cust_r = await db.execute(select(Customer).where(Customer.id == rr.customer_id))
        cust = cust_r.scalar_one_or_none()
        if cust and cust.email:
            ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == current_user.tenant_id))
            ts = ts_r.scalar_one_or_none()
            type_labels = {"refund": "退款", "return": "退货退款", "exchange": "换货"}
            template_key = "refund_approved" if action == "approve" else "refund_rejected"
            from app.tasks.email_tasks import send_notification_task
            send_notification_task.delay(
                tenant_id=current_user.tenant_id,
                template_key=template_key,
                to_email=cust.email,
                variables={
                    "name": cust.name or cust.email,
                    "order_no": rr.order_no,
                    "refund_amount": f"{float(rr.refund_amount):.2f}",
                    "refund_type": type_labels.get(rr.type, rr.type),
                    "admin_note": rr.admin_note or "",
                    "store_name": ts.store_name if ts else "SME Store",
                },
                smtp_config=build_smtp_config(ts),
            )
    except Exception:
        pass  # 邮件发送失败不影响审核流程

    return {"message": f"已{('通过' if action == 'approve' else '拒绝')}", "status": rr.status}


# ── Admin 处理完成（退款到账）─────────────────────────────────────────────

@router.post("/{refund_id}/process", summary="处理完成（退款到账）")
async def process_refund_complete(
    refund_id: int,
    body: RefundProcessIn = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.execute")),
):
    r = await db.execute(
        select(RefundRequest).where(
            RefundRequest.id == refund_id,
            RefundRequest.tenant_id == current_user.tenant_id,
        ).with_for_update()
    )
    rr = r.scalar_one_or_none()
    if not rr:
        raise HTTPException(status_code=404, detail="退款申请不存在")

    if rr.status not in ("approved", "processing"):
        raise HTTPException(status_code=400, detail=f"当前状态「{rr.status}」不可处理，仅 approved/processing 可操作")

    rr.status = "completed"
    rr.admin_id = current_user.id
    if body:
        if body.admin_note:
            rr.admin_note = (rr.admin_note or "") + ("；" + body.admin_note if rr.admin_note else body.admin_note)
        if body.tracking_no:
            rr.tracking_no = body.tracking_no
    rr.completed_at = datetime.utcnow()

    # ── 退款方式备注 ──────────────────────────────────────────────
    if body and body.refund_method:
        method_labels = {"original": "原路退回", "balance": "退至余额", "other": "其他方式"}
        method_note = f"退款方式：{method_labels.get(body.refund_method, body.refund_method)}"
        rr.admin_note = (rr.admin_note + "；" + method_note) if rr.admin_note else method_note

    # ── 订单状态 → refunded ────────────────────────────────────────
    order_r = await db.execute(select(Order).where(Order.id == rr.order_id).with_for_update())
    order = order_r.scalar_one_or_none()
    if order and order.status != "refunded":
        old_status = order.status
        order.status = "refunded"
        order_status_changed.send(
            sender=Order, order=order,
            old_status=old_status, new_status="refunded",
            tenant_id=order.tenant_id,
        )

    # ── 退货/换货 → 回退库存 ──────────────────────────────────────
    if rr.type in ("return", "exchange") and order:
        restore_items = []
        if rr.return_items:
            for ri in rr.return_items:
                restore_items.append({
                    "product_id": ri.get("product_id", ri.get("variant_id")),
                    "variant_id": ri.get("variant_id"),
                    "qty": ri.get("qty", 1),
                })
        else:
            # 未指定退货明细 → 整单恢复
            items_r = await db.execute(
                select(OrderItem).where(OrderItem.order_id == order.id)
            )
            for oi in items_r.scalars().all():
                restore_items.append({
                    "product_id": oi.product_id,
                    "variant_id": oi.variant_id,
                    "qty": oi.quantity,
                })
        if restore_items:
            # 退款单 ID 作幂等身份：部分退款各自入账、同一退款重试不重复回补
            await inv_svc.restore_stock(db, order.tenant_id, restore_items, order_id=order.id,
                                        restock=True, idem_suffix=f"rf{rr.id}")

    # 退回积分（按退款比例）
    if order:
        points_used = (order.extra_attributes or {}).get("points_used", 0)
        if points_used and rr.refund_amount and order.grand_total:
            ratio = float(rr.refund_amount) / float(order.grand_total)
            points_to_return = int(points_used * ratio)
            if points_to_return > 0:
                cust_r = await db.execute(select(Customer).where(Customer.id == rr.customer_id))
                cust = cust_r.scalar_one_or_none()
                if cust:
                    cust.points_balance += points_to_return
                    ledger = MemberPointsLedger(
                        tenant_id=order.tenant_id,
                        customer_id=cust.id,
                        order_id=order.id,
                        change_amount=points_to_return,
                        balance_after=cust.points_balance,
                        reason="退款退回积分",
                        note=f"退款申请 {rr.order_no}，退回比例 {ratio:.2%}",
                    )
                    db.add(ledger)

    await db.commit()

    # ── 邮件通知（退款完成） ──────────────────────────────────────
    try:
        cust_r = await db.execute(select(Customer).where(Customer.id == rr.customer_id))
        cust = cust_r.scalar_one_or_none()
        if cust and cust.email:
            ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == current_user.tenant_id))
            ts = ts_r.scalar_one_or_none()
            from app.tasks.email_tasks import send_notification_task
            send_notification_task.delay(
                tenant_id=current_user.tenant_id,
                template_key="refund_completed",
                to_email=cust.email,
                variables={
                    "name": cust.name or cust.email,
                    "order_no": rr.order_no,
                    "refund_amount": f"{float(rr.refund_amount):.2f}",
                    "store_name": ts.store_name if ts else "SME Store",
                },
                smtp_config=build_smtp_config(ts),
            )
    except Exception:
        pass

    return {"message": "处理完成", "status": "completed"}


# ── Admin 拒绝 ─────────────────────────────────────────────────────────────

@router.post("/{refund_id}/reject", summary="拒绝退款申请")
async def reject_refund_request(
    refund_id: int,
    body: RefundRejectIn = None,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.reject")),
):
    r = await db.execute(
        select(RefundRequest).where(
            RefundRequest.id == refund_id,
            RefundRequest.tenant_id == current_user.tenant_id,
        ).with_for_update()
    )
    rr = r.scalar_one_or_none()
    if not rr:
        raise HTTPException(status_code=404, detail="退款申请不存在")

    if rr.status != "pending":
        raise HTTPException(status_code=400, detail=f"当前状态「{rr.status}」不可拒绝，仅 pending 可操作")

    rr.status = "rejected"
    rr.admin_id = current_user.id
    if body and body.admin_note:
        rr.admin_note = body.admin_note
    rr.processed_at = datetime.utcnow()

    await db.commit()

    # ── 邮件通知（拒绝） ─────────────────────────────────────────
    try:
        cust_r = await db.execute(select(Customer).where(Customer.id == rr.customer_id))
        cust = cust_r.scalar_one_or_none()
        if cust and cust.email:
            ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == current_user.tenant_id))
            ts = ts_r.scalar_one_or_none()
            from app.tasks.email_tasks import send_notification_task
            send_notification_task.delay(
                tenant_id=current_user.tenant_id,
                template_key="refund_rejected",
                to_email=cust.email,
                variables={
                    "name": cust.name or cust.email,
                    "order_no": rr.order_no,
                    "refund_amount": f"{float(rr.refund_amount):.2f}",
                    "admin_note": rr.admin_note or "",
                    "store_name": ts.store_name if ts else "SME Store",
                },
                smtp_config=build_smtp_config(ts),
            )
    except Exception:
        pass

    return {"message": "已拒绝", "status": "rejected"}


# ── 管理员主动发起退款（直接通过，跳过 pending）────────────────────────────

class AdminRefundCreateIn(BaseModel):
    type: str = Field(..., description="refund | return | exchange")
    reason: str
    refund_amount: Decimal = Field(..., ge=Decimal("0.01"))
    return_items: Optional[list[dict]] = None   # [{"product_id":1,"variant_id":null,"qty":2}]
    admin_note: Optional[str] = None


@router.post("/orders/{order_id}/refund", summary="管理员主动发起退款（直接 approved）")
async def admin_create_refund(
    order_id: int,
    body: AdminRefundCreateIn,
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(require_permission("refunds.approve")),
):
    order_r = await db.execute(
        select(Order).where(Order.id == order_id, Order.tenant_id == current_user.tenant_id).with_for_update()
    )
    order = order_r.scalar_one_or_none()
    if not order:
        raise HTTPException(404, "订单不存在")
    if order.status not in ("paid", "shipped", "completed", "refunding"):
        raise HTTPException(400, f"订单状态「{order.status}」不可发起退款")

    # 高级编辑过的订单退款上限改为"可退余额"
    # = Σ Payment(status='completed') − Σ RefundRequest.status NOT IN ('rejected')
    # ponytail: advanced_edited 是 write-once 标记 — 该订单一旦被高级编辑就永久走
    # 新口径，旧 grand_total 与实收可能已不等。
    if order.extra_attributes and order.extra_attributes.get("advanced_edited") is True:
        completed_payments = (await db.execute(
            select(func.coalesce(func.sum(Payment.amount), 0)).where(
                Payment.order_id == order.id,
                Payment.status == "completed",
            )
        )).scalar() or 0
        refunded_total = (await db.execute(
            select(func.coalesce(func.sum(RefundRequest.refund_amount), 0)).where(
                RefundRequest.order_id == order.id,
                RefundRequest.status.notin_(["rejected"]),
            )
        )).scalar() or 0
        ceiling = Decimal(completed_payments) - Decimal(refunded_total)
        if body.refund_amount + Decimal(refunded_total) > Decimal(completed_payments):
            raise HTTPException(
                400,
                f"累计退款超过可退余额，当前可退余额：{float(ceiling):.2f}",
            )
    else:
        if body.refund_amount > order.grand_total:
            raise HTTPException(400, "退款金额不能超过订单实付金额")

        # 累计退款校验
        sum_r = await db.execute(
            select(func.coalesce(func.sum(RefundRequest.refund_amount), 0)).where(
                RefundRequest.order_id == order.id,
                RefundRequest.status.notin_(["rejected"]),
            )
        )
        existing_total = sum_r.scalar() or 0
        if existing_total + body.refund_amount > order.grand_total:
            remaining = float(order.grand_total) - float(existing_total)
            raise HTTPException(400, f"累计退款不能超过订单金额，当前可退余额：{remaining:.2f}")

    rr = RefundRequest(
        tenant_id=current_user.tenant_id,
        order_id=order.id,
        order_no=order.order_no,
        customer_id=order.customer_id,
        type=body.type,
        reason=body.reason,
        refund_amount=body.refund_amount,
        return_items=body.return_items,
        admin_note=body.admin_note,
        admin_id=current_user.id,
        status="approved",
        processed_at=datetime.utcnow(),
    )
    db.add(rr)

    if order.status not in ("refunding", "refunded"):
        old_status = order.status
        order.status = "refunding"
        order_status_changed.send(
            sender=Order, order=order,
            old_status=old_status, new_status="refunding",
            tenant_id=order.tenant_id,
        )

    await db.commit()
    await db.refresh(rr)
    return {"id": rr.id, "status": rr.status, "order_no": rr.order_no, "refund_amount": float(rr.refund_amount)}


# ── 工具函数 ──────────────────────────────────────────────────────────────

async def _format_admin_full(r: RefundRequest, db: AsyncSession) -> dict:
    # 顾客
    cust_r = await db.execute(select(Customer).where(Customer.id == r.customer_id))
    cust = cust_r.scalar_one_or_none()

    # 订单
    order_r = await db.execute(select(Order).where(Order.id == r.order_id))
    order = order_r.scalar_one_or_none()

    out = RefundRequestAdminOut(
        id=r.id,
        order_id=r.order_id,
        order_no=r.order_no,
        customer_id=r.customer_id,
        type=r.type,
        status=r.status,
        reason=r.reason,
        refund_amount=float(r.refund_amount),
        return_items=r.return_items,
        images=r.images,
        admin_note=r.admin_note,
        admin_id=r.admin_id,
        tracking_no=r.tracking_no,
        processed_at=r.processed_at.isoformat() if r.processed_at else None,
        completed_at=r.completed_at.isoformat() if r.completed_at else None,
        created_at=r.created_at.isoformat() if r.created_at else None,
        customer_name=cust.name if cust else None,
    ).model_dump()

    if order:
        out["order"] = RefundOrderSnapshot(
            order_no=order.order_no,
            status=order.status,
            grand_total=float(order.grand_total),
            created_at=order.created_at.isoformat() if order.created_at else None,
            paid_at=order.paid_at.isoformat() if order.paid_at else None,
            customer_name=cust.name if cust else None,
        ).model_dump()

    return out
