"""结账事实构建：地址、购物车行、自提判定。

Store 下单/预览与 Admin 建单/重算共用这三件事。它们原本住在 Store 路由模块里，
Admin 要用就得反向 import 一个前台路由——方向是反的，而且一旦 Store 路由改了
参数，Admin 会在运行时才发现。放到 pricing.py 旁边，两端都只依赖 core.services。

地址口径是这里最要紧的东西：预览漏取 district，报价就按"不是郊区"算，
于是预览 10 元、下单 25 元。所以取值只有这一份实现。
"""
from __future__ import annotations

from typing import Any

from sqlalchemy import select

from app.core.services import pricing as pricing_svc

#: 运费报价用到的收货地址字段（引擎 facts 里的键名）
SHIPPING_ADDRESS_FIELDS = ("country", "province", "city", "district", "zip_code")


def shipping_facts_address(body, *, prefix: str = "recv_") -> dict:
    """从请求体取运费事实用的地址。

    prefix 由调用方**显式**给出：Store 下单与 Admin 建单是 ``recv_``，
    Store 预览是裸字段名。曾经写成 ``getattr(recv_x) or getattr(x)`` 去猜，
    猜漏一个字段就静默返回空串——报价少收郊区附加费，没有任何报错。
    字段不存在时直接抛 AttributeError，让改错模型的人当场知道。
    """
    values = {}
    for name in SHIPPING_ADDRESS_FIELDS:
        attr = f"{prefix}{name}"
        if not hasattr(body, attr):
            raise AttributeError(
                f"{type(body).__name__} 缺少运费地址字段 {attr}，无法构建报价事实")
        values[name] = str(getattr(body, attr) or "")
    return values


async def build_pricing_cart_items(db, tenant_id: int, items, *, unit_split_active: bool,
                                   unit_price_of=lambda item: None):
    """请求里的购物车行 → PricingCartItem（含 unit_split 单位换算）。

    预览与下单**共用**：预览少做一次单位换算，就会用另一套小计/件数去匹配运费规则，
    于是"预览一个价、下单另一个价"。

    unit_price_of：Admin 代客下单允许人工改单价，Store 不允许——差异只有这一处，
    用一个取值函数表达，其余换算逻辑两端逐字相同。
    """
    from app.core.models.product import Product
    from app.plugins.unit_split.services import resolve_selling_unit

    cart_items = []
    for ci in items:
        unit_price_override = unit_price_of(ci)
        stock_qty_override = None
        if unit_split_active and getattr(ci, "unit_name", None):
            result = await resolve_selling_unit(db, tenant_id, ci.product_id, ci.unit_name)
            if result:
                qty_per_base, price_override = result
                stock_qty_override = ci.qty * qty_per_base
                if unit_price_override is None:
                    if price_override is not None:
                        unit_price_override = price_override
                    else:
                        # 没有整单位价：有效单价 = 基础单价 × 每单位基础数量
                        prod_r = await db.execute(
                            select(Product).where(
                                Product.id == ci.product_id,
                                Product.tenant_id == tenant_id,
                            )
                        )
                        prod = prod_r.scalar_one_or_none()
                        if prod:
                            unit_price_override = prod.base_price * qty_per_base
        cart_items.append(
            pricing_svc.PricingCartItem(
                product_id=ci.product_id,
                variant_id=getattr(ci, "variant_id", None),
                qty=ci.qty,
                unit_price_override=unit_price_override,
                stock_qty_override=stock_qty_override,
                unit_name=getattr(ci, "unit_name", None),
            )
        )
    return cart_items


def delivery_extra(*, delivery_type: Any, is_pickup: bool) -> dict:
    """写进 Order.extra_attributes 的配送事实：delivery_type + 自提核销码。

    Store 与 Admin 共用。Admin 曾经只**接受** delivery_type 却不落库，
    于是自提单：pickup-verify 查不到核销码而拒收，重算时 extra 里没有 delivery_type，
    被当成配送单重新问价，把那 0 元换成了真运费。半接线的自提事实比完全没有更坏。
    """
    import secrets

    extra: dict = {}
    if delivery_type is not None and delivery_type != "":
        try:
            extra["delivery_type"] = int(delivery_type)
        except (TypeError, ValueError):
            extra["delivery_type"] = delivery_type
    # 到店取货：生成 6 位核销码，供门店扫码核销
    if is_pickup:
        extra["pickup_code"] = f"{secrets.randbelow(1_000_000):06d}"
    return extra


async def resolve_is_pickup(db, tenant_id: int, delivery_type: Any) -> bool:
    """这单是否到店取货。delivery_type==2 且 store_pickup 插件启用才算。

    只此一处判定：三个调用点各写一遍，插件停用后总有一处还在按自提免运费。
    """
    from app.core.services.plugin_helper import is_plugin_active

    return delivery_type == 2 and await is_plugin_active("store_pickup", db, tenant_id)


__all__ = [
    "SHIPPING_ADDRESS_FIELDS",
    "build_pricing_cart_items",
    "delivery_extra",
    "resolve_is_pickup",
    "shipping_facts_address",
]
