"""Shared planner data structures."""
from __future__ import annotations

from dataclasses import dataclass, field
from decimal import Decimal
from typing import List, Optional
from uuid import UUID


@dataclass
class IngredientCost:
    ingredient_id: UUID
    qty: float
    unit: Optional[str]
    grocery_item_id: Optional[UUID]      # None when no match found
    grocery_item_name: Optional[str]
    current_price: Optional[Decimal]
    regular_price: Optional[Decimal]
    is_on_sale: bool
    estimated_cost: Decimal               # current_price scaled to recipe qty (best-effort)
    estimated_savings: Decimal            # max(regular - current, 0) scaled to qty
    matched: bool                         # False if no ingredient_grocery_match row found


@dataclass
class RecipeCost:
    recipe_id: UUID
    total_cost: Decimal
    total_savings: Decimal
    sale_ingredient_count: int
    matched_ingredient_count: int
    total_ingredient_count: int
    pantry_hit_count: int
    servings: int = 4
    line_items: List[IngredientCost] = field(default_factory=list)

    @property
    def cost_per_serving(self) -> Decimal:
        s = max(self.servings, 1)
        return (self.total_cost / Decimal(s)).quantize(Decimal("0.01"))

    @property
    def sale_coverage_pct(self) -> float:
        if self.total_ingredient_count == 0:
            return 0.0
        return self.sale_ingredient_count / self.total_ingredient_count

    @property
    def pantry_hit_pct(self) -> float:
        if self.total_ingredient_count == 0:
            return 0.0
        return self.pantry_hit_count / self.total_ingredient_count


@dataclass
class FilterResult:
    feasible_recipe_ids: List[UUID]
    rejected: dict                # recipe_id -> reason string


@dataclass
class ScoredRecipe:
    recipe_id: UUID
    score: float
    components: dict              # signal name -> raw value (for debug payload)
    cost: RecipeCost
    protein: Optional[str]
    cuisine: Optional[str]


@dataclass
class GenerationResult:
    meal_plan_id: UUID
    selected: List[ScoredRecipe]
    feasible_count: int
    rejected_summary: dict        # reason -> count
    set_score: float
