# ================================================================
# services/procurement/app/schemas/procurement_schemas.py
# ================================================================
"""
Procurement Schemas - Request/response models for procurement plans
Migrated from Orders Service with additions for local production support
"""

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


# ================================================================
# BASE SCHEMAS
# ================================================================

class ProcurementBase(BaseModel):
    """Base schema for procurement entities"""
    model_config = ConfigDict(from_attributes=True, str_strip_whitespace=True)


# ================================================================
# PROCUREMENT REQUIREMENT SCHEMAS
# ================================================================

class ProcurementRequirementBase(ProcurementBase):
    """Base procurement requirement schema"""
    product_id: uuid.UUID
    product_name: str = Field(..., min_length=1, max_length=200)
    product_sku: Optional[str] = Field(None, max_length=100)
    product_category: Optional[str] = Field(None, max_length=100)
    product_type: str = Field(default="ingredient", max_length=50)

    required_quantity: Decimal = Field(..., gt=0)
    unit_of_measure: str = Field(..., min_length=1, max_length=50)
    safety_stock_quantity: Decimal = Field(default=Decimal("0.000"), ge=0)
    total_quantity_needed: Decimal = Field(..., gt=0)

    current_stock_level: Decimal = Field(default=Decimal("0.000"), ge=0)
    reserved_stock: Decimal = Field(default=Decimal("0.000"), ge=0)
    available_stock: Decimal = Field(default=Decimal("0.000"), ge=0)
    net_requirement: Decimal = Field(..., ge=0)

    order_demand: Decimal = Field(default=Decimal("0.000"), ge=0)
    production_demand: Decimal = Field(default=Decimal("0.000"), ge=0)
    forecast_demand: Decimal = Field(default=Decimal("0.000"), ge=0)
    buffer_demand: Decimal = Field(default=Decimal("0.000"), ge=0)

    required_by_date: date
    lead_time_buffer_days: int = Field(default=1, ge=0)
    suggested_order_date: date
    latest_order_date: date

    priority: str = Field(default="normal", pattern="^(critical|high|normal|low)$")
    risk_level: str = Field(default="low", pattern="^(low|medium|high|critical)$")

    preferred_supplier_id: Optional[uuid.UUID] = None
    backup_supplier_id: Optional[uuid.UUID] = None
    supplier_name: Optional[str] = Field(None, max_length=200)
    supplier_lead_time_days: Optional[int] = Field(None, ge=0)
    minimum_order_quantity: Optional[Decimal] = Field(None, ge=0)

    estimated_unit_cost: Optional[Decimal] = Field(None, ge=0)
    estimated_total_cost: Optional[Decimal] = Field(None, ge=0)
    last_purchase_cost: Optional[Decimal] = Field(None, ge=0)


class ProcurementRequirementCreate(ProcurementRequirementBase):
    """Schema for creating procurement requirements"""
    special_requirements: Optional[str] = None
    storage_requirements: Optional[str] = Field(None, max_length=200)
    shelf_life_days: Optional[int] = Field(None, gt=0)
    quality_specifications: Optional[Dict[str, Any]] = None
    procurement_notes: Optional[str] = None

    # Smart procurement calculation metadata
    calculation_method: Optional[str] = Field(None, max_length=100)
    ai_suggested_quantity: Optional[Decimal] = Field(None, ge=0)
    adjusted_quantity: Optional[Decimal] = Field(None, ge=0)
    adjustment_reason: Optional[str] = None
    price_tier_applied: Optional[Dict[str, Any]] = None
    supplier_minimum_applied: bool = False
    storage_limit_applied: bool = False
    reorder_rule_applied: bool = False

    # NEW: Local production support fields
    is_locally_produced: bool = False
    recipe_id: Optional[uuid.UUID] = None
    parent_requirement_id: Optional[uuid.UUID] = None
    bom_explosion_level: int = Field(default=0, ge=0)


class ProcurementRequirementUpdate(ProcurementBase):
    """Schema for updating procurement requirements"""
    status: Optional[str] = Field(None, pattern="^(pending|approved|ordered|partially_received|received|cancelled)$")
    priority: Optional[str] = Field(None, pattern="^(critical|high|normal|low)$")

    approved_quantity: Optional[Decimal] = Field(None, ge=0)
    approved_cost: Optional[Decimal] = Field(None, ge=0)

    purchase_order_id: Optional[uuid.UUID] = None
    purchase_order_number: Optional[str] = Field(None, max_length=50)
    ordered_quantity: Optional[Decimal] = Field(None, ge=0)

    expected_delivery_date: Optional[date] = None
    actual_delivery_date: Optional[date] = None
    received_quantity: Optional[Decimal] = Field(None, ge=0)
    delivery_status: Optional[str] = Field(None, pattern="^(pending|in_transit|delivered|delayed|cancelled)$")

    procurement_notes: Optional[str] = None


class ProcurementRequirementResponse(ProcurementRequirementBase):
    """Schema for procurement requirement responses"""
    id: uuid.UUID
    plan_id: uuid.UUID
    requirement_number: str

    status: str
    created_at: datetime
    updated_at: datetime

    purchase_order_id: Optional[uuid.UUID] = None
    purchase_order_number: Optional[str] = None
    ordered_quantity: Decimal
    ordered_at: Optional[datetime] = None

    expected_delivery_date: Optional[date] = None
    actual_delivery_date: Optional[date] = None
    received_quantity: Decimal
    delivery_status: str

    fulfillment_rate: Optional[Decimal] = None
    on_time_delivery: Optional[bool] = None
    quality_rating: Optional[Decimal] = None

    approved_quantity: Optional[Decimal] = None
    approved_cost: Optional[Decimal] = None
    approved_at: Optional[datetime] = None
    approved_by: Optional[uuid.UUID] = None

    special_requirements: Optional[str] = None
    storage_requirements: Optional[str] = None
    shelf_life_days: Optional[int] = None
    quality_specifications: Optional[Dict[str, Any]] = None
    procurement_notes: Optional[str] = None

    # Smart procurement calculation metadata
    calculation_method: Optional[str] = None
    ai_suggested_quantity: Optional[Decimal] = None
    adjusted_quantity: Optional[Decimal] = None
    adjustment_reason: Optional[str] = None
    price_tier_applied: Optional[Dict[str, Any]] = None
    supplier_minimum_applied: bool = False
    storage_limit_applied: bool = False
    reorder_rule_applied: bool = False

    # NEW: Local production support fields
    is_locally_produced: bool = False
    recipe_id: Optional[uuid.UUID] = None
    parent_requirement_id: Optional[uuid.UUID] = None
    bom_explosion_level: int = 0


# ================================================================
# PROCUREMENT PLAN SCHEMAS
# ================================================================

class ProcurementPlanBase(ProcurementBase):
    """Base procurement plan schema"""
    plan_date: date
    plan_period_start: date
    plan_period_end: date
    planning_horizon_days: int = Field(default=14, gt=0)

    plan_type: str = Field(default="regular", pattern="^(regular|emergency|seasonal|urgent)$")
    priority: str = Field(default="normal", pattern="^(critical|high|normal|low)$")

    business_model: Optional[str] = Field(None, pattern="^(individual_bakery|central_bakery)$")
    procurement_strategy: str = Field(default="just_in_time", pattern="^(just_in_time|bulk|mixed|bulk_order)$")

    safety_stock_buffer: Decimal = Field(default=Decimal("20.00"), ge=0, le=100)
    supply_risk_level: str = Field(default="low", pattern="^(low|medium|high|critical)$")
    demand_forecast_confidence: Optional[Decimal] = Field(None, ge=1, le=10)
    seasonality_adjustment: Decimal = Field(default=Decimal("0.00"))

    special_requirements: Optional[str] = None


class ProcurementPlanCreate(ProcurementPlanBase):
    """Schema for creating procurement plans"""
    tenant_id: uuid.UUID
    requirements: Optional[List[ProcurementRequirementCreate]] = []


class ProcurementPlanUpdate(ProcurementBase):
    """Schema for updating procurement plans"""
    status: Optional[str] = Field(None, pattern="^(draft|pending_approval|approved|in_execution|completed|cancelled)$")
    priority: Optional[str] = Field(None, pattern="^(critical|high|normal|low)$")

    approved_at: Optional[datetime] = None
    approved_by: Optional[uuid.UUID] = None
    execution_started_at: Optional[datetime] = None
    execution_completed_at: Optional[datetime] = None

    special_requirements: Optional[str] = None
    seasonal_adjustments: Optional[Dict[str, Any]] = None


class ProcurementPlanResponse(ProcurementPlanBase):
    """Schema for procurement plan responses"""
    id: uuid.UUID
    tenant_id: uuid.UUID
    plan_number: str
    status: str

    total_requirements: int
    total_estimated_cost: Decimal
    total_approved_cost: Decimal
    cost_variance: Decimal

    total_demand_orders: int
    total_demand_quantity: Decimal
    total_production_requirements: Decimal

    primary_suppliers_count: int
    backup_suppliers_count: int
    supplier_diversification_score: Optional[Decimal] = None

    approved_at: Optional[datetime] = None
    approved_by: Optional[uuid.UUID] = None
    execution_started_at: Optional[datetime] = None
    execution_completed_at: Optional[datetime] = None

    fulfillment_rate: Optional[Decimal] = None
    on_time_delivery_rate: Optional[Decimal] = None
    cost_accuracy: Optional[Decimal] = None
    quality_score: Optional[Decimal] = None

    created_at: datetime
    updated_at: datetime
    created_by: Optional[uuid.UUID] = None
    updated_by: Optional[uuid.UUID] = None

    # NEW: Track forecast and production schedule links
    forecast_id: Optional[uuid.UUID] = None
    production_schedule_id: Optional[uuid.UUID] = None

    requirements: List[ProcurementRequirementResponse] = []


# ================================================================
# SUMMARY SCHEMAS
# ================================================================

class ProcurementSummary(ProcurementBase):
    """Summary of procurement plans"""
    total_plans: int
    active_plans: int
    total_requirements: int
    pending_requirements: int
    critical_requirements: int

    total_estimated_cost: Decimal
    total_approved_cost: Decimal
    cost_variance: Decimal

    average_fulfillment_rate: Optional[Decimal] = None
    average_on_time_delivery: Optional[Decimal] = None

    top_suppliers: List[Dict[str, Any]] = []
    critical_items: List[Dict[str, Any]] = []


class DashboardData(ProcurementBase):
    """Dashboard data for procurement overview"""
    current_plan: Optional[ProcurementPlanResponse] = None
    summary: ProcurementSummary

    upcoming_deliveries: List[Dict[str, Any]] = []
    overdue_requirements: List[Dict[str, Any]] = []
    low_stock_alerts: List[Dict[str, Any]] = []

    performance_metrics: Dict[str, Any] = {}


# ================================================================
# REQUEST SCHEMAS
# ================================================================

class GeneratePlanRequest(ProcurementBase):
    """Request to generate procurement plan"""
    plan_date: Optional[date] = None
    force_regenerate: bool = False
    planning_horizon_days: int = Field(default=14, gt=0, le=30)
    include_safety_stock: bool = True
    safety_stock_percentage: Decimal = Field(default=Decimal("20.00"), ge=0, le=100)


class AutoGenerateProcurementRequest(ProcurementBase):
    """
    Request to auto-generate procurement plan (called by Orchestrator)

    This is the main entry point for orchestrated procurement planning.
    The Orchestrator calls Forecasting Service first, then passes forecast data here.

    NEW: Accepts cached data snapshots from Orchestrator to eliminate duplicate API calls.
    """
    forecast_data: Dict[str, Any] = Field(..., description="Forecast data from Forecasting Service")
    production_schedule_id: Optional[uuid.UUID] = Field(None, description="Production schedule ID if available")
    target_date: Optional[date] = Field(None, description="Target date for the plan")
    planning_horizon_days: int = Field(default=14, gt=0, le=30)
    safety_stock_percentage: Decimal = Field(default=Decimal("20.00"), ge=0, le=100)
    auto_create_pos: bool = Field(True, description="Automatically create purchase orders")
    auto_approve_pos: bool = Field(False, description="Auto-approve qualifying purchase orders")

    # NEW: Cached data from Orchestrator
    inventory_data: Optional[Dict[str, Any]] = Field(None, description="Cached inventory snapshot from Orchestrator")
    suppliers_data: Optional[Dict[str, Any]] = Field(None, description="Cached suppliers snapshot from Orchestrator")
    recipes_data: Optional[Dict[str, Any]] = Field(None, description="Cached recipes snapshot from Orchestrator")


class ForecastRequest(ProcurementBase):
    """Request parameters for demand forecasting"""
    target_date: date
    horizon_days: int = Field(default=1, gt=0, le=7)
    include_confidence_intervals: bool = True
    product_ids: Optional[List[uuid.UUID]] = None


# ================================================================
# RESPONSE SCHEMAS
# ================================================================

class GeneratePlanResponse(ProcurementBase):
    """Response from plan generation"""
    success: bool
    message: str
    plan: Optional[ProcurementPlanResponse] = None
    warnings: List[str] = []
    errors: List[str] = []


class AutoGenerateProcurementResponse(ProcurementBase):
    """Response from auto-generate procurement (called by Orchestrator)"""
    success: bool
    message: str
    plan_id: Optional[uuid.UUID] = None
    plan_number: Optional[str] = None
    requirements_created: int = 0
    purchase_orders_created: int = 0
    purchase_orders_auto_approved: int = 0
    total_estimated_cost: Decimal = Decimal("0")
    warnings: List[str] = []
    errors: List[str] = []
    created_pos: List[Dict[str, Any]] = []


class PaginatedProcurementPlans(ProcurementBase):
    """Paginated list of procurement plans"""
    plans: List[ProcurementPlanResponse]
    total: int
    page: int
    limit: int
    has_more: bool
