# services/suppliers/app/repositories/supplier_repository.py
"""
Supplier repository for database operations
"""

from typing import List, Optional, Dict, Any
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import and_, or_, func, select
from uuid import UUID
from datetime import datetime

from app.models.suppliers import Supplier, SupplierStatus, SupplierType
from app.repositories.base import BaseRepository


class SupplierRepository(BaseRepository[Supplier]):
    """Repository for supplier management operations"""
    
    def __init__(self, db: AsyncSession):
        super().__init__(Supplier, db)
    
    async def get_by_name(self, tenant_id: UUID, name: str) -> Optional[Supplier]:
        """Get supplier by name within tenant"""
        stmt = select(self.model).filter(
            and_(
                self.model.tenant_id == tenant_id,
                self.model.name == name
            )
        )
        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()
    
    async def get_by_supplier_code(self, tenant_id: UUID, supplier_code: str) -> Optional[Supplier]:
        """Get supplier by supplier code within tenant"""
        stmt = select(self.model).filter(
            and_(
                self.model.tenant_id == tenant_id,
                self.model.supplier_code == supplier_code
            )
        )
        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()
    
    async def search_suppliers(
        self,
        tenant_id: UUID,
        search_term: Optional[str] = None,
        supplier_type: Optional[SupplierType] = None,
        status: Optional[SupplierStatus] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[Supplier]:
        """Search suppliers with filters"""
        stmt = select(self.model).filter(self.model.tenant_id == tenant_id)

        # Search term filter (name, contact person, email)
        if search_term:
            search_filter = or_(
                self.model.name.ilike(f"%{search_term}%"),
                self.model.contact_person.ilike(f"%{search_term}%"),
                self.model.email.ilike(f"%{search_term}%")
            )
            stmt = stmt.filter(search_filter)

        # Type filter
        if supplier_type:
            stmt = stmt.filter(self.model.supplier_type == supplier_type)

        # Status filter
        if status:
            stmt = stmt.filter(self.model.status == status)

        stmt = stmt.order_by(self.model.name).limit(limit).offset(offset)
        result = await self.db.execute(stmt)
        return result.scalars().all()
    
    async def get_active_suppliers(self, tenant_id: UUID) -> List[Supplier]:
        """Get all active suppliers for a tenant"""
        stmt = select(self.model).filter(
            and_(
                self.model.tenant_id == tenant_id,
                self.model.status == SupplierStatus.active
            )
        ).order_by(self.model.name)
        result = await self.db.execute(stmt)
        return result.scalars().all()

    async def get_suppliers_by_ids(self, tenant_id: UUID, supplier_ids: List[UUID]) -> List[Supplier]:
        """Get multiple suppliers by IDs in a single query (batch fetch)"""
        if not supplier_ids:
            return []

        stmt = select(self.model).filter(
            and_(
                self.model.tenant_id == tenant_id,
                self.model.id.in_(supplier_ids)
            )
        ).order_by(self.model.name)
        result = await self.db.execute(stmt)
        return result.scalars().all()
    
    def get_suppliers_by_type(
        self, 
        tenant_id: UUID, 
        supplier_type: SupplierType
    ) -> List[Supplier]:
        """Get suppliers by type"""
        return (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.supplier_type == supplier_type,
                    self.model.status == SupplierStatus.active
                )
            )
            .order_by(self.model.quality_rating.desc(), self.model.name)
            .all()
        )
    
    def get_top_suppliers(
        self, 
        tenant_id: UUID, 
        limit: int = 10
    ) -> List[Supplier]:
        """Get top suppliers by quality rating and order value"""
        return (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status == SupplierStatus.active
                )
            )
            .order_by(
                self.model.quality_rating.desc(),
                self.model.total_amount.desc()
            )
            .limit(limit)
            .all()
        )
    
    def update_supplier_stats(
        self,
        supplier_id: UUID,
        total_orders_increment: int = 0,
        total_amount_increment: float = 0.0,
        new_quality_rating: Optional[float] = None,
        new_delivery_rating: Optional[float] = None
    ) -> Optional[Supplier]:
        """Update supplier performance statistics"""
        supplier = self.get_by_id(supplier_id)
        if not supplier:
            return None
        
        # Update counters
        if total_orders_increment:
            supplier.total_orders += total_orders_increment
        
        if total_amount_increment:
            supplier.total_amount += total_amount_increment
        
        # Update ratings (these should be calculated averages)
        if new_quality_rating is not None:
            supplier.quality_rating = new_quality_rating
        
        if new_delivery_rating is not None:
            supplier.delivery_rating = new_delivery_rating
        
        supplier.updated_at = datetime.utcnow()
        
        self.db.commit()
        self.db.refresh(supplier)
        return supplier
    
    def get_suppliers_needing_review(
        self,
        tenant_id: UUID,
        days_since_last_order: int = 30
    ) -> List[Supplier]:
        """Get suppliers that may need performance review"""
        from datetime import datetime, timedelta
        cutoff_date = datetime.utcnow() - timedelta(days=days_since_last_order)
        
        return (
            self.db.query(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status == SupplierStatus.active,
                    or_(
                        self.model.quality_rating < 3.0,  # Poor rating
                        self.model.delivery_rating < 3.0,  # Poor delivery
                        self.model.updated_at < cutoff_date  # Long time since interaction
                    )
                )
            )
            .order_by(self.model.quality_rating.asc())
            .all()
        )
    
    async def get_supplier_statistics(self, tenant_id: UUID) -> Dict[str, Any]:
        """Get supplier statistics for dashboard"""
        total_suppliers = await self.count_by_tenant(tenant_id)

        # Get all suppliers for this tenant to avoid multiple queries and enum casting issues
        all_stmt = select(self.model).filter(self.model.tenant_id == tenant_id)
        all_result = await self.db.execute(all_stmt)
        all_suppliers = all_result.scalars().all()

        # Calculate statistics in Python to avoid database enum casting issues
        active_suppliers = [s for s in all_suppliers if s.status == SupplierStatus.active]
        pending_suppliers = [s for s in all_suppliers if s.status == SupplierStatus.pending_approval]

        # Calculate averages from active suppliers
        quality_ratings = [s.quality_rating for s in active_suppliers if s.quality_rating and s.quality_rating > 0]
        avg_quality_rating = sum(quality_ratings) / len(quality_ratings) if quality_ratings else 0.0

        delivery_ratings = [s.delivery_rating for s in active_suppliers if s.delivery_rating and s.delivery_rating > 0]
        avg_delivery_rating = sum(delivery_ratings) / len(delivery_ratings) if delivery_ratings else 0.0

        # Total spend for all suppliers
        total_spend = sum(float(s.total_amount or 0) for s in all_suppliers)

        return {
            "total_suppliers": total_suppliers,
            "active_suppliers": len(active_suppliers),
            "pending_suppliers": len(pending_suppliers),
            "avg_quality_rating": round(float(avg_quality_rating), 2),
            "avg_delivery_rating": round(float(avg_delivery_rating), 2),
            "total_spend": float(total_spend)
        }
    
    async def approve_supplier(
        self,
        supplier_id: UUID,
        approved_by: UUID,
        approval_date: Optional[datetime] = None
    ) -> Optional[Supplier]:
        """Approve a pending supplier"""
        supplier = await self.get_by_id(supplier_id)
        if not supplier or supplier.status != SupplierStatus.pending_approval:
            return None

        supplier.status = SupplierStatus.active
        supplier.approved_by = approved_by
        supplier.approved_at = approval_date or datetime.utcnow()
        supplier.rejection_reason = None
        supplier.updated_at = datetime.utcnow()

        await self.db.commit()
        await self.db.refresh(supplier)
        return supplier
    
    async def reject_supplier(
        self,
        supplier_id: UUID,
        rejection_reason: str,
        approved_by: UUID
    ) -> Optional[Supplier]:
        """Reject a pending supplier"""
        supplier = await self.get_by_id(supplier_id)
        if not supplier or supplier.status != SupplierStatus.pending_approval:
            return None

        supplier.status = SupplierStatus.inactive
        supplier.rejection_reason = rejection_reason
        supplier.approved_by = approved_by
        supplier.approved_at = datetime.utcnow()
        supplier.updated_at = datetime.utcnow()

        await self.db.commit()
        await self.db.refresh(supplier)
        return supplier
    async def hard_delete_supplier(self, supplier_id: UUID) -> Dict[str, Any]:
        """
        Hard delete supplier and all associated data
        Returns counts of deleted records
        """
        from app.models.suppliers import (
            SupplierPriceList, SupplierQualityReview,
            SupplierAlert, SupplierScorecard, PurchaseOrderStatus, PurchaseOrder
        )
        from app.models.performance import SupplierPerformanceMetric
        from sqlalchemy import delete

        # Get supplier first
        supplier = await self.get_by_id(supplier_id)
        if not supplier:
            return None

        # Check for active purchase orders (block deletion if any exist)
        active_statuses = [
            PurchaseOrderStatus.draft,
            PurchaseOrderStatus.pending_approval,
            PurchaseOrderStatus.approved,
            PurchaseOrderStatus.sent_to_supplier,
            PurchaseOrderStatus.confirmed
        ]

        stmt = select(PurchaseOrder).where(
            PurchaseOrder.supplier_id == supplier_id,
            PurchaseOrder.status.in_(active_statuses)
        )
        result = await self.db.execute(stmt)
        active_pos = result.scalars().all()

        if active_pos:
            raise ValueError(
                f"Cannot delete supplier with {len(active_pos)} active purchase orders. "
                "Complete or cancel all purchase orders first."
            )

        # Count related records before deletion
        stmt = select(SupplierPriceList).where(SupplierPriceList.supplier_id == supplier_id)
        result = await self.db.execute(stmt)
        price_lists_count = len(result.scalars().all())

        stmt = select(SupplierQualityReview).where(SupplierQualityReview.supplier_id == supplier_id)
        result = await self.db.execute(stmt)
        quality_reviews_count = len(result.scalars().all())

        stmt = select(SupplierPerformanceMetric).where(SupplierPerformanceMetric.supplier_id == supplier_id)
        result = await self.db.execute(stmt)
        metrics_count = len(result.scalars().all())

        stmt = select(SupplierAlert).where(SupplierAlert.supplier_id == supplier_id)
        result = await self.db.execute(stmt)
        alerts_count = len(result.scalars().all())

        stmt = select(SupplierScorecard).where(SupplierScorecard.supplier_id == supplier_id)
        result = await self.db.execute(stmt)
        scorecards_count = len(result.scalars().all())

        # Delete related records (in reverse dependency order)
        stmt = delete(SupplierScorecard).where(SupplierScorecard.supplier_id == supplier_id)
        await self.db.execute(stmt)

        stmt = delete(SupplierAlert).where(SupplierAlert.supplier_id == supplier_id)
        await self.db.execute(stmt)

        stmt = delete(SupplierPerformanceMetric).where(SupplierPerformanceMetric.supplier_id == supplier_id)
        await self.db.execute(stmt)

        stmt = delete(SupplierQualityReview).where(SupplierQualityReview.supplier_id == supplier_id)
        await self.db.execute(stmt)

        stmt = delete(SupplierPriceList).where(SupplierPriceList.supplier_id == supplier_id)
        await self.db.execute(stmt)

        # Delete the supplier itself
        await self.delete(supplier_id)

        await self.db.commit()

        return {
            "supplier_name": supplier.name,
            "deleted_price_lists": price_lists_count,
            "deleted_quality_reviews": quality_reviews_count,
            "deleted_performance_metrics": metrics_count,
            "deleted_alerts": alerts_count,
            "deleted_scorecards": scorecards_count,
            "deletion_timestamp": datetime.utcnow()
        }

    async def get_supplier_price_lists(
        self,
        supplier_id: UUID,
        tenant_id: UUID,
        is_active: bool = True
    ) -> List[Any]:
        """Get all price list items for a supplier"""
        from app.models.suppliers import SupplierPriceList
        
        stmt = select(SupplierPriceList).filter(
            and_(
                SupplierPriceList.supplier_id == supplier_id,
                SupplierPriceList.tenant_id == tenant_id
            )
        )
        
        if is_active:
            stmt = stmt.filter(SupplierPriceList.is_active == True)
        
        result = await self.db.execute(stmt)
        return result.scalars().all()

    async def get_supplier_price_list(
        self,
        price_list_id: UUID,
        tenant_id: UUID
    ) -> Optional[Any]:
        """Get specific price list item"""
        from app.models.suppliers import SupplierPriceList
        
        stmt = select(SupplierPriceList).filter(
            and_(
                SupplierPriceList.id == price_list_id,
                SupplierPriceList.tenant_id == tenant_id
            )
        )
        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def create_supplier_price_list(
        self,
        create_data: Dict[str, Any]
    ) -> Any:
        """Create a new price list item"""
        from app.models.suppliers import SupplierPriceList
        
        price_list = SupplierPriceList(**create_data)
        self.db.add(price_list)
        await self.db.commit()
        await self.db.refresh(price_list)
        return price_list

    async def update_supplier_price_list(
        self,
        price_list_id: UUID,
        update_data: Dict[str, Any]
    ) -> Any:
        """Update a price list item"""
        from app.models.suppliers import SupplierPriceList
        
        stmt = select(SupplierPriceList).filter(SupplierPriceList.id == price_list_id)
        result = await self.db.execute(stmt)
        price_list = result.scalar_one_or_none()
        
        if not price_list:
            raise ValueError("Price list item not found")
        
        # Update fields
        for key, value in update_data.items():
            if hasattr(price_list, key):
                setattr(price_list, key, value)
        
        await self.db.commit()
        await self.db.refresh(price_list)
        return price_list

    async def delete_supplier_price_list(
        self,
        price_list_id: UUID
    ) -> bool:
        """Delete a price list item"""
        from app.models.suppliers import SupplierPriceList
        from sqlalchemy import delete
        
        stmt = delete(SupplierPriceList).filter(SupplierPriceList.id == price_list_id)
        result = await self.db.execute(stmt)
        
        await self.db.commit()
        return result.rowcount > 0
