# services/suppliers/app/repositories/purchase_order_item_repository.py
"""
Purchase Order Item repository for database operations
"""

from typing import List, Optional, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import and_, func
from uuid import UUID
from datetime import datetime

from app.models.suppliers import PurchaseOrderItem
from app.repositories.base import BaseRepository


class PurchaseOrderItemRepository(BaseRepository[PurchaseOrderItem]):
    """Repository for purchase order item operations"""
    
    def __init__(self, db: Session):
        super().__init__(PurchaseOrderItem, db)
    
    def get_by_purchase_order(self, po_id: UUID) -> List[PurchaseOrderItem]:
        """Get all items for a purchase order"""
        return (
            self.db.query(self.model)
            .filter(self.model.purchase_order_id == po_id)
            .order_by(self.model.created_at)
            .all()
        )
    
    def get_by_inventory_product(
        self, 
        tenant_id: UUID, 
        inventory_product_id: UUID,
        limit: int = 20
    ) -> List[PurchaseOrderItem]:
        """Get recent order items for a specific inventory product"""
        return (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.inventory_product_id == inventory_product_id
                )
            )
            .order_by(self.model.created_at.desc())
            .limit(limit)
            .all()
        )
    
    def update_received_quantity(
        self,
        item_id: UUID,
        received_quantity: int,
        update_remaining: bool = True
    ) -> Optional[PurchaseOrderItem]:
        """Update received quantity for an item"""
        item = self.get_by_id(item_id)
        if not item:
            return None
        
        item.received_quantity = max(0, received_quantity)
        
        if update_remaining:
            item.remaining_quantity = max(0, item.ordered_quantity - item.received_quantity)
        
        item.updated_at = datetime.utcnow()
        
        self.db.commit()
        self.db.refresh(item)
        return item
    
    def add_received_quantity(
        self,
        item_id: UUID,
        quantity_to_add: int
    ) -> Optional[PurchaseOrderItem]:
        """Add to received quantity (for partial deliveries)"""
        item = self.get_by_id(item_id)
        if not item:
            return None
        
        new_received = item.received_quantity + quantity_to_add
        new_received = min(new_received, item.ordered_quantity)  # Cap at ordered quantity
        
        return self.update_received_quantity(item_id, new_received)
    
    def get_partially_received_items(self, tenant_id: UUID) -> List[PurchaseOrderItem]:
        """Get items that are partially received (have remaining quantity)"""
        return (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.received_quantity > 0,
                    self.model.remaining_quantity > 0
                )
            )
            .order_by(self.model.updated_at.desc())
            .all()
        )
    
    def get_pending_receipt_items(
        self, 
        tenant_id: UUID,
        inventory_product_id: Optional[UUID] = None
    ) -> List[PurchaseOrderItem]:
        """Get items pending receipt (not yet delivered)"""
        query = (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.remaining_quantity > 0
                )
            )
        )
        
        if inventory_product_id:
            query = query.filter(self.model.inventory_product_id == inventory_product_id)
        
        return query.order_by(self.model.created_at).all()
    
    def calculate_line_total(self, item_id: UUID) -> Optional[PurchaseOrderItem]:
        """Recalculate line total for an item"""
        item = self.get_by_id(item_id)
        if not item:
            return None
        
        item.line_total = item.ordered_quantity * item.unit_price
        item.updated_at = datetime.utcnow()
        
        self.db.commit()
        self.db.refresh(item)
        return item
    
    def get_inventory_product_purchase_history(
        self,
        tenant_id: UUID,
        inventory_product_id: UUID,
        days_back: int = 90
    ) -> Dict[str, Any]:
        """Get purchase history and analytics for an inventory product"""
        from datetime import timedelta
        
        cutoff_date = datetime.utcnow() - timedelta(days=days_back)
        
        # Get items within date range
        items = (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.inventory_product_id == inventory_product_id,
                    self.model.created_at >= cutoff_date
                )
            )
            .all()
        )
        
        if not items:
            return {
                "total_quantity_ordered": 0,
                "total_amount_spent": 0.0,
                "average_unit_price": 0.0,
                "order_count": 0,
                "last_order_date": None,
                "price_trend": "stable"
            }
        
        # Calculate statistics
        total_quantity = sum(item.ordered_quantity for item in items)
        total_amount = sum(float(item.line_total) for item in items)
        order_count = len(items)
        avg_unit_price = total_amount / total_quantity if total_quantity > 0 else 0
        last_order_date = max(item.created_at for item in items)
        
        # Price trend analysis (simple)
        if order_count >= 2:
            sorted_items = sorted(items, key=lambda x: x.created_at)
            first_half = sorted_items[:order_count//2]
            second_half = sorted_items[order_count//2:]
            
            avg_price_first = sum(float(item.unit_price) for item in first_half) / len(first_half)
            avg_price_second = sum(float(item.unit_price) for item in second_half) / len(second_half)
            
            if avg_price_second > avg_price_first * 1.1:
                price_trend = "increasing"
            elif avg_price_second < avg_price_first * 0.9:
                price_trend = "decreasing"
            else:
                price_trend = "stable"
        else:
            price_trend = "insufficient_data"
        
        return {
            "total_quantity_ordered": total_quantity,
            "total_amount_spent": round(total_amount, 2),
            "average_unit_price": round(avg_unit_price, 4),
            "order_count": order_count,
            "last_order_date": last_order_date,
            "price_trend": price_trend
        }
    
    def get_top_purchased_inventory_products(
        self,
        tenant_id: UUID,
        days_back: int = 30,
        limit: int = 10
    ) -> List[Dict[str, Any]]:
        """Get most purchased inventory products by quantity or value"""
        from datetime import timedelta
        
        cutoff_date = datetime.utcnow() - timedelta(days=days_back)
        
        # Group by inventory product and calculate totals
        results = (
            self.db.query(
                self.model.inventory_product_id,
                self.model.unit_of_measure,
                func.sum(self.model.ordered_quantity).label('total_quantity'),
                func.sum(self.model.line_total).label('total_amount'),
                func.count(self.model.id).label('order_count'),
                func.avg(self.model.unit_price).label('avg_unit_price')
            )
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.created_at >= cutoff_date
                )
            )
            .group_by(
                self.model.inventory_product_id,
                self.model.unit_of_measure
            )
            .order_by(func.sum(self.model.line_total).desc())
            .limit(limit)
            .all()
        )
        
        return [
            {
                "inventory_product_id": str(row.inventory_product_id),
                "unit_of_measure": row.unit_of_measure,
                "total_quantity": int(row.total_quantity),
                "total_amount": round(float(row.total_amount), 2),
                "order_count": int(row.order_count),
                "avg_unit_price": round(float(row.avg_unit_price), 4)
            }
            for row in results
        ]
    
    def bulk_update_items(
        self,
        po_id: UUID,
        item_updates: List[Dict[str, Any]]
    ) -> List[PurchaseOrderItem]:
        """Bulk update multiple items in a purchase order"""
        updated_items = []
        
        for update_data in item_updates:
            item_id = update_data.get('id')
            if not item_id:
                continue
            
            item = (
                self.db.query(self.model)
                .filter(
                    and_(
                        self.model.id == item_id,
                        self.model.purchase_order_id == po_id
                    )
                )
                .first()
            )
            
            if item:
                # Update allowed fields
                for key, value in update_data.items():
                    if key != 'id' and hasattr(item, key):
                        setattr(item, key, value)
                
                # Recalculate line total if quantity or price changed
                if 'ordered_quantity' in update_data or 'unit_price' in update_data:
                    item.line_total = item.ordered_quantity * item.unit_price
                    item.remaining_quantity = item.ordered_quantity - item.received_quantity
                
                item.updated_at = datetime.utcnow()
                updated_items.append(item)
        
        self.db.commit()
        
        # Refresh all items
        for item in updated_items:
            self.db.refresh(item)
        
        return updated_items