"""Pricing service — price lookup and order price computation.

Price resolution cascade for order lines:
  1. OutputType's linked pricing_tier (if active) → use its price_per_item
  2. Product's category_key → look up PricingTier by category
  3. "default" category tier → global fallback
  4. None if nothing configured
"""
from decimal import Decimal
from typing import Any

from sqlalchemy import select, update as sql_update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.domains.billing.models import PricingTier


async def get_price_for(
    db: AsyncSession,
    category_key: str,
    quality_level: str = "Normal",
) -> Decimal | None:
    """Return price_per_item for the given category + quality level."""
    # 1. Exact match
    result = await db.execute(
        select(PricingTier).where(
            PricingTier.category_key == category_key,
            PricingTier.quality_level == quality_level,
            PricingTier.is_active.is_(True),
        )
    )
    tier = result.scalar_one_or_none()
    if tier is not None:
        return tier.price_per_item

    if category_key == "default":
        return None

    # 2. Fallback: default category
    result = await db.execute(
        select(PricingTier).where(
            PricingTier.category_key == "default",
            PricingTier.quality_level == quality_level,
            PricingTier.is_active.is_(True),
        )
    )
    tier = result.scalar_one_or_none()
    return tier.price_per_item if tier is not None else None


async def resolve_line_price(
    db: AsyncSession,
    output_type_id: str | None,
    product_category_key: str | None,
) -> Decimal | None:
    """Resolve the unit price for a single order line using the cascade."""
    if output_type_id is not None:
        from app.domains.rendering.models import OutputType
        result = await db.execute(
            select(OutputType)
            .options(selectinload(OutputType.pricing_tier))
            .where(OutputType.id == output_type_id)
        )
        ot = result.scalar_one_or_none()
        if ot and ot.pricing_tier and ot.pricing_tier.is_active:
            return ot.pricing_tier.price_per_item

    # Step 2+3: category lookup with default fallback
    cat = product_category_key or "default"
    return await get_price_for(db, cat)


async def estimate_order_price(
    db: AsyncSession,
    lines: list[dict[str, Any]],
) -> dict:
    """Estimate price for a list of prospective order lines."""
    from app.domains.products.models import Product

    breakdown: list[dict] = []
    total = Decimal("0.00")
    has_unpriced = False

    for line in lines:
        product_id = line.get("product_id")
        output_type_id = line.get("output_type_id")

        # Get product category
        cat = None
        if product_id:
            prod_result = await db.execute(
                select(Product).where(Product.id == product_id)
            )
            prod = prod_result.scalar_one_or_none()
            if prod:
                cat = prod.category_key

        price = await resolve_line_price(db, output_type_id, cat)

        breakdown.append({
            "output_type_id": str(output_type_id) if output_type_id else None,
            "product_id": str(product_id) if product_id else None,
            "unit_price": float(price) if price is not None else None,
        })

        if price is not None:
            total += price
        else:
            has_unpriced = True

    return {
        "total": float(total),
        "line_count": len(lines),
        "breakdown": breakdown,
        "has_unpriced": has_unpriced,
    }


async def refresh_order_price(db: AsyncSession, order_id) -> Decimal | None:
    """Re-fetch order + lines, resolve per-line prices, snapshot to unit_price, update order total."""
    from app.domains.orders.models import Order, OrderLine
    from app.domains.rendering.models import OutputType

    order_result = await db.execute(select(Order).where(Order.id == order_id))
    order = order_result.scalar_one_or_none()
    if order is None:
        return None

    lines_result = await db.execute(
        select(OrderLine)
        .options(
            selectinload(OrderLine.output_type).selectinload(OutputType.pricing_tier),
            selectinload(OrderLine.product),
        )
        .where(
            OrderLine.order_id == order_id,
            OrderLine.output_type_id.is_not(None),
        )
    )
    lines = lines_result.scalars().all()

    if not lines:
        await db.execute(
            sql_update(Order)
            .where(Order.id == order_id)
            .values(estimated_price=Decimal("0.00"))
        )
        await db.commit()
        return Decimal("0.00")

    total = Decimal("0.00")
    any_priced = False

    for line in lines:
        # Cascade: 1) OT pricing tier, 2) product category, 3) default
        price = None
        if line.output_type and line.output_type.pricing_tier and line.output_type.pricing_tier.is_active:
            price = line.output_type.pricing_tier.price_per_item
        else:
            cat = line.product.category_key if line.product else None
            price = await get_price_for(db, cat or "default")

        # Snapshot to line
        await db.execute(
            sql_update(OrderLine)
            .where(OrderLine.id == line.id)
            .values(unit_price=price)
        )

        if price is not None:
            total += price
            any_priced = True

    new_price = total if any_priced else None

    await db.execute(
        sql_update(Order)
        .where(Order.id == order_id)
        .values(estimated_price=new_price)
    )
    await db.commit()
    return new_price
