"""代理余额服务：冻结、扣除、返还、结算收益。"""
from datetime import datetime, timezone

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.agent_balance_log import AgentBalanceChangeType, AgentBalanceLog
from app.models.user_agent import UserAgent


async def get_agent_for_update(db: AsyncSession, user_id: int) -> UserAgent | None:
    result = await db.execute(
        select(UserAgent)
        .where(UserAgent.user_id == user_id)
        .with_for_update()
    )
    return result.scalar_one_or_none()


async def _log(
    db: AsyncSession,
    *,
    user_id: int,
    change_type: AgentBalanceChangeType,
    delta: int,
    agent: UserAgent,
    order_id: int | None = None,
    ref_no: str | None = None,
    remark: str | None = None,
    operator_id: int | None = None,
) -> AgentBalanceLog:
    log = AgentBalanceLog(
        user_id=user_id,
        change_type=change_type,
        delta=delta,
        balance_after=agent.agent_balance,
        frozen_balance_after=agent.agent_frozen_balance,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark,
        operator_id=operator_id,
    )
    db.add(log)
    await db.flush()
    return log


async def freeze_subsidy(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """下单时冻结代理补贴金额。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")
    if agent.agent_balance < amount:
        raise ValueError("代理可用余额不足，无法补贴该订单")

    agent.agent_balance -= amount
    agent.agent_frozen_balance += amount

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.SUBSIDY_FREEZE,
        delta=-amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 冻结代理补贴 {amount} 分",
    )
    await db.flush()
    return agent


async def charge_subsidy(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """服务开通成功后，将冻结的补贴转为实际扣除。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")
    if agent.agent_frozen_balance < amount:
        raise ValueError("代理冻结余额不足")

    agent.agent_frozen_balance -= amount
    agent.total_subsidy += amount

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.SUBSIDY_CHARGE,
        delta=-amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 扣除代理补贴 {amount} 分",
    )
    await db.flush()
    return agent


async def release_subsidy(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """服务开通前取消/退款，释放冻结的补贴。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")
    if agent.agent_frozen_balance < amount:
        raise ValueError("代理冻结余额不足")

    agent.agent_frozen_balance -= amount
    agent.agent_balance += amount

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.SUBSIDY_RELEASE,
        delta=amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 释放代理补贴 {amount} 分",
    )
    await db.flush()
    return agent


async def refund_subsidy(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """服务开通后退款，将已扣除的补贴返还代理余额。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")

    agent.agent_balance += amount
    agent.total_subsidy = max(0, agent.total_subsidy - amount)

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.SUBSIDY_REFUND,
        delta=amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 退款返还代理补贴 {amount} 分",
    )
    await db.flush()
    return agent


async def credit_profit(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """代理收益结算，入账到代理可用余额。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")

    agent.agent_balance += amount
    agent.total_profit += amount

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.PROFIT_SETTLE,
        delta=amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 结算代理收益 {amount} 分",
    )
    await db.flush()
    return agent


async def reverse_profit(
    db: AsyncSession,
    *,
    agent_user_id: int,
    amount: int,
    order_id: int,
    ref_no: str,
    remark: str | None = None,
) -> UserAgent:
    """退款时扣回已结算的代理收益。余额不足允许负数。"""
    agent = await get_agent_for_update(db, agent_user_id)
    if not agent:
        raise ValueError("代理身份不存在")

    agent.agent_balance -= amount
    agent.total_profit = max(0, agent.total_profit - amount)

    await _log(
        db,
        user_id=agent_user_id,
        change_type=AgentBalanceChangeType.PROFIT_REVERSE,
        delta=-amount,
        agent=agent,
        order_id=order_id,
        ref_no=ref_no,
        remark=remark or f"订单 {ref_no} 退款扣回代理收益 {amount} 分",
    )
    await db.flush()
    return agent


async def get_balance_summary(db: AsyncSession, user_id: int) -> dict:
    result = await db.execute(select(UserAgent).where(UserAgent.user_id == user_id))
    agent = result.scalar_one_or_none()
    if not agent:
        return {"agent_balance": 0, "agent_frozen_balance": 0}
    return {
        "agent_balance": agent.agent_balance,
        "agent_frozen_balance": agent.agent_frozen_balance,
    }
