from typing import Any

from pydantic import BaseModel, Field


class ComponentDetail(BaseModel):
    product_type: str
    ratio: float
    supplier: str
    price_per_gallon: float
    terminal: str | None = None
    contract_id: str | None = None
    curve_id: str | None = None


BlendComponentDetail = ComponentDetail


class SiteAssignment(BaseModel):
    site_id: str
    tank_id: str
    contract_id: str
    terminal: str
    supplier: str = Field(default="Unknown", description="Supplier providing the price")
    product_type: str
    volume_assigned: float
    supply_cost: float
    transport_cost: float
    freight_cost: float = Field(default=0.0, description="Freight cost component")
    toll_cost: float = Field(default=0.0, description="Toll cost component")
    total_cost: float
    curve_id: str | None = None
    is_blend: bool = Field(default=False)
    blend_components: list[ComponentDetail] | None = Field(default=None)
    components: list[ComponentDetail]


class FakeSupplyUsage(BaseModel):
    contract_id: str
    fake_supply_volume: float = Field(default=0.0)
    fake_demand_volume: float = Field(default=0.0)
    fake_supply_cost: float = Field(default=0.0)
    fake_demand_cost: float = Field(default=0.0)


class OptimizationResult(BaseModel):
    assignments: list[SiteAssignment]
    total_cost: float
    total_volume: float
    contracts_utilized: list[str]
    unassigned_sites: list[str] = Field(default_factory=list)
    fake_supply_usage: list[FakeSupplyUsage] = Field(default_factory=list)

    @property
    def cost_per_gallon(self) -> float:
        return self.total_cost / self.total_volume if self.total_volume > 0 else 0

    @property
    def utilization_summary(self) -> dict[str, int]:
        return {
            "total_assignments": len(self.assignments),
            "contracts_used": len(self.contracts_utilized),
            "unassigned_sites": len(self.unassigned_sites),
            "contracts_with_fake_supply": len(
                [f for f in self.fake_supply_usage if f.fake_supply_volume > 0]
            ),
            "contracts_with_fake_demand": len(
                [f for f in self.fake_supply_usage if f.fake_demand_volume > 0]
            ),
        }


class ContractAssignment(BaseModel):
    """Individual store/product assignment within a contract"""
    site_id: str
    product_type: str
    volume_assigned: float


class ContractUtilization(BaseModel):
    """Utilization details for a specific contract"""
    contract_id: str
    terminal: str
    supplier: str
    products: str
    target_volume: str
    target_volume_min: float
    target_volume_max: float
    assigned_volume: float
    status: str
    assignments_count: int
    assignments: list[ContractAssignment] = Field(default_factory=list)
    avg_cost_per_gal: float
    total_cost: float


class TankAssignmentDetail(BaseModel):
    """Detailed tank assignment with per-gallon costs"""
    site_id: str
    tank_id: str
    contract_id: str
    terminal: str
    supplier: str
    product_type: str
    volume_assigned: float
    supply_price_per_gal: float
    freight_rate_per_gal: float
    toll_rate_per_gal: float
    total_transport_cost_per_gal: float
    total_cost_per_gal: float
    total_assignment_cost: float
    curve_id: str | None = None
    is_blend: bool = Field(default=False)
    blend_components: list[ComponentDetail] | None = Field(default=None)
    components: list[ComponentDetail]


class OptimizationMetadata(BaseModel):
    """Metadata about the optimization run"""
    runtime: float
    fulfillment_rate: float
    utilization_rate: float
    gap: float
    total_contract_volume: float
    total_tank_demand: float
    solver_name: str = "auto"
    status: str = "Optimal"
    objective_value: float | None = None
    objective_bound: float | None = None


class OptimizationAPIResponse(BaseModel):
    """Complete API response including results and formatted outputs"""
    result: OptimizationResult
    metadata: OptimizationMetadata
    tank_assignments: list[TankAssignmentDetail]
    contract_analysis: list[ContractUtilization]
    summary: dict[str, Any]
    solver_log: str
    validation_issues: list[str] = Field(default_factory=list)
    logs: list[str] = Field(default_factory=list)
