"""Compute per-recipe cost and savings against ingredient_grocery_match.

Inputs:
    ingredients: list[dict] from recipe.ingredients JSONB
    match_index: dict[ingredient_id, list[match_dict]] — pre-fetched, sorted by confidence DESC
    pantry_ingredient_ids: set of ingredient_ids in home_pantry
    ingredient_name_index: optional dict[ingredient_id, name_lower] for unit conversion

Strategy:
    For each ingredient in the recipe, take the top-confidence match
    (or skip if none). Cost = current_price × converted_qty.
    Unit conversion is attempted via app.utils.units.convert_qty; if
    the units are incommensurable, falls back to dimensionless qty to
    preserve the monotonic ranking signal.
    Savings = max(regular - current, 0) × converted_qty.
"""
from __future__ import annotations

from decimal import Decimal
from typing import Dict, Iterable, List, Optional, Set
from uuid import UUID

from app.services.planner.types import IngredientCost, RecipeCost
from app.utils.units import convert_qty


def _decimal(v) -> Decimal:
    if v is None:
        return Decimal("0")
    return v if isinstance(v, Decimal) else Decimal(str(v))


def _scale(price: Decimal, qty: float) -> Decimal:
    return (price * Decimal(str(qty))).quantize(Decimal("0.01"))


def compute_recipe_cost(
    *,
    recipe_id: UUID,
    ingredients: Iterable[dict],
    match_index: Dict[UUID, List[dict]],
    pantry_ingredient_ids: Set[UUID],
    servings: int = 4,
) -> RecipeCost:
    line_items: List[IngredientCost] = []
    total_cost = Decimal("0.00")
    total_savings = Decimal("0.00")
    sale_count = 0
    matched_count = 0
    pantry_hits = 0
    total = 0

    for raw in ingredients:
        total += 1
        ing_id = raw["ingredient_id"]
        if isinstance(ing_id, str):
            ing_id = UUID(ing_id)
        qty = float(raw.get("qty") or 1.0)
        unit = raw.get("unit")

        if ing_id in pantry_ingredient_ids:
            pantry_hits += 1

        candidates = match_index.get(ing_id) or []
        if not candidates:
            line_items.append(
                IngredientCost(
                    ingredient_id=ing_id,
                    qty=qty,
                    unit=unit,
                    grocery_item_id=None,
                    grocery_item_name=None,
                    current_price=None,
                    regular_price=None,
                    is_on_sale=False,
                    estimated_cost=Decimal("0.00"),
                    estimated_savings=Decimal("0.00"),
                    matched=False,
                )
            )
            continue

        best = candidates[0]
        current = _decimal(best.get("current_price"))
        regular = _decimal(best.get("regular_price"))
        is_on_sale = bool(best.get("is_on_sale"))
        converted_qty = float(
            convert_qty(
                qty,
                unit,
                best.get("grocery_unit"),
                ingredient_name_lower=best.get("ingredient_name"),
            )
        )
        line_cost = _scale(current, converted_qty)
        line_savings = _scale(max(regular - current, Decimal("0")), converted_qty)

        matched_count += 1
        if is_on_sale:
            sale_count += 1
        total_cost += line_cost
        total_savings += line_savings

        line_items.append(
            IngredientCost(
                ingredient_id=ing_id,
                qty=qty,
                unit=unit,
                grocery_item_id=best.get("grocery_item_id"),
                grocery_item_name=best.get("grocery_item_name"),
                current_price=current,
                regular_price=regular,
                is_on_sale=is_on_sale,
                estimated_cost=line_cost,
                estimated_savings=line_savings,
                matched=True,
            )
        )

    return RecipeCost(
        recipe_id=recipe_id,
        total_cost=total_cost.quantize(Decimal("0.01")),
        total_savings=total_savings.quantize(Decimal("0.01")),
        sale_ingredient_count=sale_count,
        matched_ingredient_count=matched_count,
        total_ingredient_count=total,
        pantry_hit_count=pantry_hits,
        servings=max(servings, 1),
        line_items=line_items,
    )
