"""
InventoryEvent model for comprehensive audit logging of all inventory operations.

This is an append-only event log (event sourcing pattern). Every inventory
change is recorded as an immutable event, enabling:
- Full audit trail
- Debugging oversells
- Replaying history to rebuild state
- Compliance requirements

Events are NEVER updated or deleted - only appended.
"""

import uuid
from sqlalchemy import Column, String, Integer, DateTime, Index, Enum as SAEnum, Text, JSON
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.sql import func
from enum import Enum

from ..base import Base


class InventoryEventType(str, Enum):
    """Types of inventory events."""
    
    # Reservation events (from orderbox)
    RESERVED = "reserved"                   # Order placed, inventory reserved
    RESERVE_FAILED = "reserve_failed"       # Reservation attempt failed (insufficient stock)
    RELEASED_FULFILLED = "released_fulfilled"   # Order fulfilled, reservation released
    RELEASED_CANCELLED = "released_cancelled"   # Order cancelled, reservation released
    RELEASED_EXPIRED = "released_expired"       # Reservation auto-expired
    
    # ERP sync events (from storehouse)
    ERP_UPDATE = "erp_update"               # ERP sent inventory update
    ERP_BULK_UPDATE = "erp_bulk_update"     # ERP bulk update batch
    
    # Reconciliation events
    RECONCILIATION_FIX = "reconciliation_fix"   # Fixed discrepancy
    RECONCILIATION_ALERT = "reconciliation_alert"  # Discrepancy detected but not fixed
    
    # Manual events
    MANUAL_ADJUSTMENT = "manual_adjustment"     # Admin manual adjustment
    
    # System events
    REDIS_REBUILD = "redis_rebuild"         # Redis state rebuilt from DB
    SNAPSHOT = "snapshot"                   # Periodic state snapshot


class InventoryEvent(Base):
    """
    Immutable event log for all inventory operations.
    
    Every change to inventory state is recorded here. This enables:
    - "What happened to SKU X between time A and B?"
    - "Why did this oversell happen?"
    - "Which orders affected inventory for SKU X?"
    - Rebuilding Redis state from events
    
    Example events:
    
    1. Order CLK12345 reserves 2x SKU-CREAM on WH001:
       event_type=RESERVED, order_id=CLK12345, sku=SKU-CREAM, 
       warehouse_id=WH001, quantity_change=-2, available_after=48
    
    2. ERP updates SKU-CREAM on WH001 to 100:
       event_type=ERP_UPDATE, sku=SKU-CREAM, warehouse_id=WH001,
       erp_quantity=100, available_after=95 (accounting for 5 reserved)
    """
    __tablename__ = "inventory_event"
    __table_args__ = (
        # Fast lookup by SKU + warehouse + time (most common query)
        Index('idx_event_sku_warehouse_time', 'sku', 'warehouse_id', 'created_at'),
        # Fast lookup by order
        Index('idx_event_order', 'order_id'),
        # Fast lookup by event type (for analytics)
        Index('idx_event_type_time', 'event_type', 'created_at'),
        # For finding events by source
        Index('idx_event_source', 'source'),
        # Time-based partitioning support
        Index('idx_event_created', 'created_at'),
    )

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
    
    # Event identification
    event_type = Column(SAEnum(InventoryEventType, values_callable=lambda x: [e.value for e in x]), nullable=False)
    source = Column(String(50), nullable=False)  # 'medusa', 'shopify', 'erp', 'system', 'admin'
    
    # What was affected
    sku = Column(String(100), nullable=False)
    warehouse_id = Column(String(100), nullable=False)
    
    # Order context (for reservation events)
    order_id = Column(String(100), nullable=True, index=True)  # Internal order ID
    order_display_id = Column(String(100), nullable=True)  # Display ID like CLK12345
    platform_order_id = Column(String(100), nullable=True)  # Platform-specific ID (Medusa/Shopify order ID)
    line_item_id = Column(String(100), nullable=True)  # Line item ID for multi-SKU orders
    reservation_id = Column(UUID(as_uuid=True), nullable=True)  # FK to InventoryReservation
    
    # Quantity information
    quantity_change = Column(Integer, nullable=True)  # Delta: negative=reserved, positive=released
    erp_quantity = Column(Integer, nullable=True)     # For ERP_UPDATE events
    
    # State snapshot AFTER this event
    available_after = Column(Integer, nullable=True)
    reserved_medusa_after = Column(Integer, nullable=True)
    reserved_shopify_after = Column(Integer, nullable=True)
    erp_quantity_after = Column(Integer, nullable=True)
    
    # Additional context
    reason = Column(String(100), nullable=True)  # e.g., 'fulfilled', 'cancelled', 'insufficient_stock'
    event_metadata = Column(JSON, nullable=True)  # Flexible additional data
    error_message = Column(Text, nullable=True)  # For failed events
    
    # Tracing
    correlation_id = Column(String(100), nullable=True)  # For tracing across services
    event_id = Column(String(100), nullable=True)  # ERP webhook event_id
    
    # Timestamp (append-only, never updated)
    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)

    def __repr__(self):
        return (
            f"<InventoryEvent("
            f"type={self.event_type.value}, "
            f"sku={self.sku}, "
            f"warehouse={self.warehouse_id}, "
            f"change={self.quantity_change}, "
            f"order={self.order_id}"
            f")>"
        )


# Convenience factory functions for creating events
def create_reserve_event(
    sku: str,
    warehouse_id: str,
    quantity: int,
    source: str,
    order_id: str,
    order_display_id: str = None,
    platform_order_id: str = None,
    line_item_id: str = None,
    reservation_id: uuid.UUID = None,
    available_after: int = None,
    reserved_medusa_after: int = None,
    reserved_shopify_after: int = None,
    correlation_id: str = None,
) -> InventoryEvent:
    """Create a RESERVED event."""
    return InventoryEvent(
        event_type=InventoryEventType.RESERVED,
        source=source,
        sku=sku,
        warehouse_id=warehouse_id,
        order_id=order_id,
        order_display_id=order_display_id,
        platform_order_id=platform_order_id,
        line_item_id=line_item_id,
        reservation_id=reservation_id,
        quantity_change=-quantity,  # Negative = removed from available
        available_after=available_after,
        reserved_medusa_after=reserved_medusa_after,
        reserved_shopify_after=reserved_shopify_after,
        correlation_id=correlation_id,
    )


def create_release_event(
    sku: str,
    warehouse_id: str,
    quantity: int,
    source: str,
    reason: str,  # 'fulfilled' or 'cancelled'
    order_id: str = None,
    order_display_id: str = None,
    platform_order_id: str = None,
    line_item_id: str = None,
    reservation_id: uuid.UUID = None,
    available_after: int = None,
    reserved_medusa_after: int = None,
    reserved_shopify_after: int = None,
    correlation_id: str = None,
) -> InventoryEvent:
    """Create a RELEASED event."""
    event_type = (
        InventoryEventType.RELEASED_FULFILLED 
        if reason == "fulfilled" 
        else InventoryEventType.RELEASED_CANCELLED
    )
    return InventoryEvent(
        event_type=event_type,
        source=source,
        sku=sku,
        warehouse_id=warehouse_id,
        order_id=order_id,
        order_display_id=order_display_id,
        platform_order_id=platform_order_id,
        line_item_id=line_item_id,
        reservation_id=reservation_id,
        quantity_change=quantity,  # Positive = returned to available
        reason=reason,
        available_after=available_after,
        reserved_medusa_after=reserved_medusa_after,
        reserved_shopify_after=reserved_shopify_after,
        correlation_id=correlation_id,
    )


def create_erp_update_event(
    sku: str,
    warehouse_id: str,
    erp_quantity: int,
    available_after: int,
    reserved_medusa_after: int = None,
    reserved_shopify_after: int = None,
    event_id: str = None,
    correlation_id: str = None,
) -> InventoryEvent:
    """Create an ERP_UPDATE event."""
    return InventoryEvent(
        event_type=InventoryEventType.ERP_UPDATE,
        source="erp",
        sku=sku,
        warehouse_id=warehouse_id,
        erp_quantity=erp_quantity,
        erp_quantity_after=erp_quantity,
        available_after=available_after,
        reserved_medusa_after=reserved_medusa_after,
        reserved_shopify_after=reserved_shopify_after,
        event_id=event_id,
        correlation_id=correlation_id,
    )
