"""
Stock Receipt Models for Inventory Service
Lot-level tracking for deliveries with expiration dates
"""

from sqlalchemy import Column, String, DateTime, Float, Integer, Text, Index, Boolean, Numeric, ForeignKey, Enum as SQLEnum, Date
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
import uuid
import enum
from datetime import datetime, timezone, date
from typing import Dict, Any, Optional, List
from decimal import Decimal

from shared.database.base import Base


class ReceiptStatus(enum.Enum):
    """Stock receipt status values"""
    DRAFT = "draft"
    CONFIRMED = "confirmed"
    CANCELLED = "cancelled"


class StockReceipt(Base):
    """
    Stock receipt tracking for purchase order deliveries
    Captures lot-level details and expiration dates
    """
    __tablename__ = "stock_receipts"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)

    # Purchase order reference
    po_id = Column(UUID(as_uuid=True), nullable=False, index=True)
    po_number = Column(String(100), nullable=True)  # Denormalized for quick reference

    # Receipt details
    received_at = Column(DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
    received_by_user_id = Column(UUID(as_uuid=True), nullable=True)

    # Status
    status = Column(
        SQLEnum(ReceiptStatus, name='receiptstatus', create_type=True),
        nullable=False,
        default=ReceiptStatus.DRAFT,
        index=True
    )

    # Supplier information (denormalized)
    supplier_id = Column(UUID(as_uuid=True), nullable=True, index=True)
    supplier_name = Column(String(255), nullable=True)

    # Overall notes
    notes = Column(Text, nullable=True)
    has_discrepancies = Column(Boolean, default=False, nullable=False)

    # Timestamps
    created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
    updated_at = Column(DateTime(timezone=True),
                       default=lambda: datetime.now(timezone.utc),
                       onupdate=lambda: datetime.now(timezone.utc))
    confirmed_at = Column(DateTime(timezone=True), nullable=True)

    # Relationships
    line_items = relationship("StockReceiptLineItem", back_populates="receipt", cascade="all, delete-orphan")

    __table_args__ = (
        Index('idx_stock_receipts_tenant_status', 'tenant_id', 'status'),
        Index('idx_stock_receipts_po', 'po_id'),
        Index('idx_stock_receipts_received_at', 'tenant_id', 'received_at'),
        Index('idx_stock_receipts_supplier', 'supplier_id', 'received_at'),
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary for API responses"""
        return {
            'id': str(self.id),
            'tenant_id': str(self.tenant_id),
            'po_id': str(self.po_id),
            'po_number': self.po_number,
            'received_at': self.received_at.isoformat() if self.received_at else None,
            'received_by_user_id': str(self.received_by_user_id) if self.received_by_user_id else None,
            'status': self.status.value if isinstance(self.status, enum.Enum) else self.status,
            'supplier_id': str(self.supplier_id) if self.supplier_id else None,
            'supplier_name': self.supplier_name,
            'notes': self.notes,
            'has_discrepancies': self.has_discrepancies,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None,
            'confirmed_at': self.confirmed_at.isoformat() if self.confirmed_at else None,
            'line_items': [item.to_dict() for item in self.line_items] if self.line_items else [],
        }


class StockReceiptLineItem(Base):
    """
    Individual line items in a stock receipt
    One line item per product, with multiple lots possible
    """
    __tablename__ = "stock_receipt_line_items"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
    receipt_id = Column(UUID(as_uuid=True), ForeignKey('stock_receipts.id', ondelete='CASCADE'), nullable=False, index=True)

    # Product information
    ingredient_id = Column(UUID(as_uuid=True), nullable=False, index=True)
    ingredient_name = Column(String(255), nullable=True)  # Denormalized

    # PO line reference
    po_line_id = Column(UUID(as_uuid=True), nullable=True)

    # Quantities
    expected_quantity = Column(Numeric(10, 2), nullable=False)
    actual_quantity = Column(Numeric(10, 2), nullable=False)
    unit_of_measure = Column(String(20), nullable=False)

    # Discrepancy tracking
    has_discrepancy = Column(Boolean, default=False, nullable=False)
    discrepancy_reason = Column(Text, nullable=True)

    # Cost tracking
    unit_cost = Column(Numeric(10, 2), nullable=True)
    total_cost = Column(Numeric(10, 2), nullable=True)

    # Timestamps
    created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
    updated_at = Column(DateTime(timezone=True),
                       default=lambda: datetime.now(timezone.utc),
                       onupdate=lambda: datetime.now(timezone.utc))

    # Relationships
    receipt = relationship("StockReceipt", back_populates="line_items")
    lots = relationship("StockLot", back_populates="line_item", cascade="all, delete-orphan")

    __table_args__ = (
        Index('idx_line_items_receipt', 'receipt_id'),
        Index('idx_line_items_ingredient', 'ingredient_id'),
        Index('idx_line_items_discrepancy', 'tenant_id', 'has_discrepancy'),
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary for API responses"""
        return {
            'id': str(self.id),
            'tenant_id': str(self.tenant_id),
            'receipt_id': str(self.receipt_id),
            'ingredient_id': str(self.ingredient_id),
            'ingredient_name': self.ingredient_name,
            'po_line_id': str(self.po_line_id) if self.po_line_id else None,
            'expected_quantity': float(self.expected_quantity) if self.expected_quantity else None,
            'actual_quantity': float(self.actual_quantity) if self.actual_quantity else None,
            'unit_of_measure': self.unit_of_measure,
            'has_discrepancy': self.has_discrepancy,
            'discrepancy_reason': self.discrepancy_reason,
            'unit_cost': float(self.unit_cost) if self.unit_cost else None,
            'total_cost': float(self.total_cost) if self.total_cost else None,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None,
            'lots': [lot.to_dict() for lot in self.lots] if self.lots else [],
        }


class StockLot(Base):
    """
    Individual lots within a line item
    Critical for tracking expiration dates when deliveries are split
    """
    __tablename__ = "stock_lots"

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
    line_item_id = Column(UUID(as_uuid=True), ForeignKey('stock_receipt_line_items.id', ondelete='CASCADE'), nullable=False, index=True)

    # Links to stock table (created on confirmation)
    stock_id = Column(UUID(as_uuid=True), nullable=True, index=True)

    # Lot identification
    lot_number = Column(String(100), nullable=True)
    supplier_lot_number = Column(String(100), nullable=True)

    # Quantity for this lot
    quantity = Column(Numeric(10, 2), nullable=False)
    unit_of_measure = Column(String(20), nullable=False)

    # Critical: Expiration tracking
    expiration_date = Column(Date, nullable=False, index=True)
    best_before_date = Column(Date, nullable=True)

    # Storage location
    warehouse_location = Column(String(100), nullable=True)
    storage_zone = Column(String(50), nullable=True)

    # Quality notes
    quality_notes = Column(Text, nullable=True)

    # Timestamps
    created_at = Column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc))
    updated_at = Column(DateTime(timezone=True),
                       default=lambda: datetime.now(timezone.utc),
                       onupdate=lambda: datetime.now(timezone.utc))

    # Relationships
    line_item = relationship("StockReceiptLineItem", back_populates="lots")

    __table_args__ = (
        Index('idx_lots_line_item', 'line_item_id'),
        Index('idx_lots_stock', 'stock_id'),
        Index('idx_lots_expiration', 'tenant_id', 'expiration_date'),
        Index('idx_lots_lot_number', 'tenant_id', 'lot_number'),
    )

    def to_dict(self) -> Dict[str, Any]:
        """Convert model to dictionary for API responses"""
        return {
            'id': str(self.id),
            'tenant_id': str(self.tenant_id),
            'line_item_id': str(self.line_item_id),
            'stock_id': str(self.stock_id) if self.stock_id else None,
            'lot_number': self.lot_number,
            'supplier_lot_number': self.supplier_lot_number,
            'quantity': float(self.quantity) if self.quantity else None,
            'unit_of_measure': self.unit_of_measure,
            'expiration_date': self.expiration_date.isoformat() if self.expiration_date else None,
            'best_before_date': self.best_before_date.isoformat() if self.best_before_date else None,
            'warehouse_location': self.warehouse_location,
            'storage_zone': self.storage_zone,
            'quality_notes': self.quality_notes,
            'created_at': self.created_at.isoformat() if self.created_at else None,
            'updated_at': self.updated_at.isoformat() if self.updated_at else None,
        }
