"""
Pydantic schemas for replenishment planning.
"""

from pydantic import BaseModel, Field, validator
from typing import List, Optional, Dict, Any
from datetime import date, datetime
from decimal import Decimal
from uuid import UUID


# ============================================================================
# Replenishment Plan Schemas
# ============================================================================

class ReplenishmentPlanItemBase(BaseModel):
    """Base schema for replenishment plan item"""
    ingredient_id: UUID
    ingredient_name: str
    unit_of_measure: str

    base_quantity: Decimal
    safety_stock_quantity: Decimal
    shelf_life_adjusted_quantity: Decimal
    final_order_quantity: Decimal

    order_date: date
    delivery_date: date
    required_by_date: date

    lead_time_days: int
    is_urgent: bool
    urgency_reason: Optional[str] = None
    waste_risk: str
    stockout_risk: str

    supplier_id: Optional[UUID] = None

    safety_stock_calculation: Optional[Dict[str, Any]] = None
    shelf_life_adjustment: Optional[Dict[str, Any]] = None
    inventory_projection: Optional[Dict[str, Any]] = None


class ReplenishmentPlanItemCreate(ReplenishmentPlanItemBase):
    """Schema for creating replenishment plan item"""
    replenishment_plan_id: UUID


class ReplenishmentPlanItemResponse(ReplenishmentPlanItemBase):
    """Schema for replenishment plan item response"""
    id: UUID
    replenishment_plan_id: UUID
    created_at: datetime

    class Config:
        from_attributes = True


class ReplenishmentPlanBase(BaseModel):
    """Base schema for replenishment plan"""
    planning_date: date
    projection_horizon_days: int = 7

    forecast_id: Optional[UUID] = None
    production_schedule_id: Optional[UUID] = None

    total_items: int
    urgent_items: int
    high_risk_items: int
    total_estimated_cost: Decimal


class ReplenishmentPlanCreate(ReplenishmentPlanBase):
    """Schema for creating replenishment plan"""
    tenant_id: UUID
    items: List[Dict[str, Any]] = []


class ReplenishmentPlanResponse(ReplenishmentPlanBase):
    """Schema for replenishment plan response"""
    id: UUID
    tenant_id: UUID
    status: str
    created_at: datetime
    updated_at: Optional[datetime] = None
    executed_at: Optional[datetime] = None

    items: List[ReplenishmentPlanItemResponse] = []

    class Config:
        from_attributes = True


class ReplenishmentPlanSummary(BaseModel):
    """Summary schema for list views"""
    id: UUID
    tenant_id: UUID
    planning_date: date
    total_items: int
    urgent_items: int
    high_risk_items: int
    total_estimated_cost: Decimal
    status: str
    created_at: datetime

    class Config:
        from_attributes = True


# ============================================================================
# Inventory Projection Schemas
# ============================================================================

class InventoryProjectionBase(BaseModel):
    """Base schema for inventory projection"""
    ingredient_id: UUID
    ingredient_name: str
    projection_date: date

    starting_stock: Decimal
    forecasted_consumption: Decimal
    scheduled_receipts: Decimal
    projected_ending_stock: Decimal

    is_stockout: bool
    coverage_gap: Decimal


class InventoryProjectionCreate(InventoryProjectionBase):
    """Schema for creating inventory projection"""
    tenant_id: UUID
    replenishment_plan_id: Optional[UUID] = None


class InventoryProjectionResponse(InventoryProjectionBase):
    """Schema for inventory projection response"""
    id: UUID
    tenant_id: UUID
    replenishment_plan_id: Optional[UUID] = None
    created_at: datetime

    class Config:
        from_attributes = True


class IngredientProjectionSummary(BaseModel):
    """Summary of projections for one ingredient"""
    ingredient_id: UUID
    ingredient_name: str
    current_stock: Decimal
    unit_of_measure: str
    projection_horizon_days: int
    total_consumption: Decimal
    total_receipts: Decimal
    stockout_days: int
    stockout_risk: str
    daily_projections: List[Dict[str, Any]]


# ============================================================================
# Supplier Allocation Schemas
# ============================================================================

class SupplierAllocationBase(BaseModel):
    """Base schema for supplier allocation"""
    supplier_id: UUID
    supplier_name: str

    allocation_type: str
    allocated_quantity: Decimal
    allocation_percentage: Decimal

    unit_price: Decimal
    total_cost: Decimal
    lead_time_days: int

    supplier_score: Decimal
    score_breakdown: Optional[Dict[str, float]] = None
    allocation_reason: Optional[str] = None


class SupplierAllocationCreate(SupplierAllocationBase):
    """Schema for creating supplier allocation"""
    replenishment_plan_item_id: Optional[UUID] = None
    requirement_id: Optional[UUID] = None


class SupplierAllocationResponse(SupplierAllocationBase):
    """Schema for supplier allocation response"""
    id: UUID
    replenishment_plan_item_id: Optional[UUID] = None
    requirement_id: Optional[UUID] = None
    created_at: datetime

    class Config:
        from_attributes = True


# ============================================================================
# Supplier Selection Schemas
# ============================================================================

class SupplierSelectionRequest(BaseModel):
    """Request to select suppliers for an ingredient"""
    ingredient_id: UUID
    ingredient_name: str
    required_quantity: Decimal
    supplier_options: List[Dict[str, Any]]


class SupplierSelectionResult(BaseModel):
    """Result of supplier selection"""
    ingredient_id: UUID
    ingredient_name: str
    required_quantity: Decimal
    allocations: List[Dict[str, Any]]
    total_cost: Decimal
    weighted_lead_time: float
    risk_score: float
    diversification_applied: bool
    selection_strategy: str


# ============================================================================
# Replenishment Planning Request Schemas
# ============================================================================

class IngredientRequirementInput(BaseModel):
    """Input for a single ingredient requirement"""
    ingredient_id: UUID
    ingredient_name: str
    required_quantity: Decimal
    required_by_date: date

    supplier_id: Optional[UUID] = None
    lead_time_days: int = 3
    shelf_life_days: Optional[int] = None
    is_perishable: bool = False
    category: str = 'dry'
    unit_of_measure: str = 'kg'

    current_stock: Decimal = Decimal('0')
    daily_consumption_rate: float = 0.0
    demand_std_dev: float = 0.0


class GenerateReplenishmentPlanRequest(BaseModel):
    """Request to generate replenishment plan"""
    tenant_id: UUID
    requirements: List[IngredientRequirementInput]
    forecast_id: Optional[UUID] = None
    production_schedule_id: Optional[UUID] = None

    projection_horizon_days: int = 7
    service_level: float = 0.95
    buffer_days: int = 1


class GenerateReplenishmentPlanResponse(BaseModel):
    """Response from generating replenishment plan"""
    plan_id: UUID
    tenant_id: UUID
    planning_date: date
    projection_horizon_days: int

    total_items: int
    urgent_items: int
    high_risk_items: int
    total_estimated_cost: Decimal

    created_at: datetime

    items: List[Dict[str, Any]]


# ============================================================================
# MOQ Aggregation Schemas
# ============================================================================

class MOQAggregationRequest(BaseModel):
    """Request for MOQ aggregation"""
    requirements: List[Dict[str, Any]]
    supplier_constraints: Dict[str, Dict[str, Any]]


class MOQAggregationResponse(BaseModel):
    """Response from MOQ aggregation"""
    aggregated_orders: List[Dict[str, Any]]
    efficiency_metrics: Dict[str, Any]


# ============================================================================
# Safety Stock Calculation Schemas
# ============================================================================

class SafetyStockRequest(BaseModel):
    """Request for safety stock calculation"""
    ingredient_id: UUID
    daily_demands: List[float]
    lead_time_days: int
    service_level: float = 0.95


class SafetyStockResponse(BaseModel):
    """Response from safety stock calculation"""
    safety_stock_quantity: Decimal
    service_level: float
    z_score: float
    demand_std_dev: float
    lead_time_days: int
    calculation_method: str
    confidence: str
    reasoning: str


# ============================================================================
# Inventory Projection Request Schemas
# ============================================================================

class ProjectInventoryRequest(BaseModel):
    """Request to project inventory"""
    ingredient_id: UUID
    ingredient_name: str
    current_stock: Decimal
    unit_of_measure: str
    daily_demand: List[Dict[str, Any]]
    scheduled_receipts: List[Dict[str, Any]] = []
    projection_horizon_days: int = 7


class ProjectInventoryResponse(BaseModel):
    """Response from inventory projection"""
    ingredient_id: UUID
    ingredient_name: str
    current_stock: Decimal
    unit_of_measure: str
    projection_horizon_days: int
    total_consumption: Decimal
    total_receipts: Decimal
    stockout_days: int
    stockout_risk: str
    daily_projections: List[Dict[str, Any]]


# ============================================================================
# Supplier Selection History Schemas
# ============================================================================

class SupplierSelectionHistoryBase(BaseModel):
    """Base schema for supplier selection history"""
    ingredient_id: UUID
    ingredient_name: str
    selected_supplier_id: UUID
    selected_supplier_name: str

    selection_date: date
    quantity: Decimal
    unit_price: Decimal
    total_cost: Decimal

    lead_time_days: int
    quality_score: Optional[Decimal] = None
    delivery_performance: Optional[Decimal] = None

    selection_strategy: str
    was_primary_choice: bool = True


class SupplierSelectionHistoryCreate(SupplierSelectionHistoryBase):
    """Schema for creating supplier selection history"""
    tenant_id: UUID


class SupplierSelectionHistoryResponse(SupplierSelectionHistoryBase):
    """Schema for supplier selection history response"""
    id: UUID
    tenant_id: UUID
    created_at: datetime

    class Config:
        from_attributes = True


# ============================================================================
# Analytics Schemas
# ============================================================================

class ReplenishmentAnalytics(BaseModel):
    """Analytics for replenishment planning"""
    total_plans: int
    total_items_planned: int
    total_estimated_value: Decimal

    urgent_items_percentage: float
    high_risk_items_percentage: float

    average_lead_time_days: float
    average_safety_stock_percentage: float

    stockout_prevention_rate: float
    moq_optimization_savings: Decimal

    supplier_diversification_rate: float
    average_suppliers_per_ingredient: float


class InventoryProjectionAnalytics(BaseModel):
    """Analytics for inventory projections"""
    total_ingredients: int
    stockout_ingredients: int
    stockout_percentage: float

    risk_breakdown: Dict[str, int]

    total_stockout_days: int
    total_consumption: Decimal
    total_receipts: Decimal

    projection_horizon_days: int


# ============================================================================
# Validators
# ============================================================================

@validator('required_quantity', 'current_stock', 'allocated_quantity',
           'safety_stock_quantity', 'base_quantity', 'final_order_quantity')
def validate_positive_quantity(cls, v):
    """Validate that quantities are positive"""
    if v < 0:
        raise ValueError('Quantity must be non-negative')
    return v


@validator('service_level')
def validate_service_level(cls, v):
    """Validate service level is between 0 and 1"""
    if not 0 <= v <= 1:
        raise ValueError('Service level must be between 0 and 1')
    return v
