# services/suppliers/app/repositories/purchase_order_repository.py
"""
Purchase Order repository for database operations (Async SQLAlchemy 2.0)
"""

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

from app.models.suppliers import (
    PurchaseOrder, PurchaseOrderItem, PurchaseOrderStatus,
    Supplier, SupplierPriceList
)
from app.repositories.base import BaseRepository


class PurchaseOrderRepository(BaseRepository[PurchaseOrder]):
    """Repository for purchase order management operations"""

    def __init__(self, db: AsyncSession):
        super().__init__(PurchaseOrder, db)

    async def get_by_po_number(self, tenant_id: UUID, po_number: str) -> Optional[PurchaseOrder]:
        """Get purchase order by PO number within tenant"""
        stmt = select(self.model).filter(
            and_(
                self.model.tenant_id == tenant_id,
                self.model.po_number == po_number
            )
        )
        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def get_with_items(self, po_id: UUID) -> Optional[PurchaseOrder]:
        """Get purchase order with all items and supplier loaded"""
        stmt = (
            select(self.model)
            .options(
                selectinload(self.model.items),
                selectinload(self.model.supplier)
            )
            .filter(self.model.id == po_id)
        )
        result = await self.db.execute(stmt)
        return result.scalar_one_or_none()

    async def search_purchase_orders(
        self,
        tenant_id: UUID,
        supplier_id: Optional[UUID] = None,
        status: Optional[PurchaseOrderStatus] = None,
        priority: Optional[str] = None,
        date_from: Optional[datetime] = None,
        date_to: Optional[datetime] = None,
        search_term: Optional[str] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[PurchaseOrder]:
        """Search purchase orders with comprehensive filters"""
        stmt = (
            select(self.model)
            .options(selectinload(self.model.supplier))
            .filter(self.model.tenant_id == tenant_id)
        )

        # Supplier filter
        if supplier_id:
            stmt = stmt.filter(self.model.supplier_id == supplier_id)

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

        # Priority filter
        if priority:
            stmt = stmt.filter(self.model.priority == priority)

        # Date range filter
        if date_from:
            stmt = stmt.filter(self.model.order_date >= date_from)
        if date_to:
            stmt = stmt.filter(self.model.order_date <= date_to)

        # Search term filter (PO number, reference)
        if search_term:
            search_filter = or_(
                self.model.po_number.ilike(f"%{search_term}%"),
                self.model.reference_number.ilike(f"%{search_term}%")
            )
            stmt = stmt.filter(search_filter)

        stmt = stmt.order_by(desc(self.model.order_date)).limit(limit).offset(offset)

        result = await self.db.execute(stmt)
        return list(result.scalars().all())

    async def get_orders_by_status(
        self,
        tenant_id: UUID,
        status: PurchaseOrderStatus,
        limit: int = 100
    ) -> List[PurchaseOrder]:
        """Get purchase orders by status"""
        stmt = (
            select(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status == status
                )
            )
            .order_by(desc(self.model.order_date))
            .limit(limit)
        )
        result = await self.db.execute(stmt)
        return list(result.scalars().all())

    async def get_pending_approval(
        self,
        tenant_id: UUID,
        limit: int = 50
    ) -> List[PurchaseOrder]:
        """Get purchase orders pending approval"""
        return await self.get_orders_by_status(
            tenant_id,
            PurchaseOrderStatus.pending_approval,
            limit
        )

    async def get_by_supplier(
        self,
        tenant_id: UUID,
        supplier_id: UUID,
        limit: int = 100,
        offset: int = 0
    ) -> List[PurchaseOrder]:
        """Get purchase orders for a specific supplier"""
        stmt = (
            select(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.supplier_id == supplier_id
                )
            )
            .order_by(desc(self.model.order_date))
            .limit(limit)
            .offset(offset)
        )
        result = await self.db.execute(stmt)
        return list(result.scalars().all())

    async def get_orders_requiring_delivery(
        self,
        tenant_id: UUID,
        days_ahead: int = 7
    ) -> List[PurchaseOrder]:
        """Get orders expecting delivery within specified days"""
        cutoff_date = datetime.utcnow().date() + timedelta(days=days_ahead)

        stmt = (
            select(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status.in_([
                        PurchaseOrderStatus.approved,
                        PurchaseOrderStatus.sent_to_supplier,
                        PurchaseOrderStatus.confirmed
                    ]),
                    self.model.required_delivery_date <= cutoff_date
                )
            )
            .order_by(self.model.required_delivery_date)
        )
        result = await self.db.execute(stmt)
        return list(result.scalars().all())

    async def get_overdue_deliveries(self, tenant_id: UUID) -> List[PurchaseOrder]:
        """Get purchase orders with overdue deliveries"""
        today = datetime.utcnow().date()

        stmt = (
            select(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status.in_([
                        PurchaseOrderStatus.approved,
                        PurchaseOrderStatus.sent_to_supplier,
                        PurchaseOrderStatus.confirmed
                    ]),
                    self.model.required_delivery_date < today
                )
            )
            .order_by(self.model.required_delivery_date)
        )
        result = await self.db.execute(stmt)
        return list(result.scalars().all())

    async def get_total_spent_by_supplier(
        self,
        tenant_id: UUID,
        supplier_id: UUID,
        date_from: Optional[datetime] = None,
        date_to: Optional[datetime] = None
    ) -> float:
        """Calculate total amount spent with a supplier"""
        stmt = (
            select(func.sum(self.model.total_amount))
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.supplier_id == supplier_id,
                    self.model.status.in_([
                        PurchaseOrderStatus.approved,
                        PurchaseOrderStatus.sent_to_supplier,
                        PurchaseOrderStatus.confirmed,
                        PurchaseOrderStatus.received,
                        PurchaseOrderStatus.completed
                    ])
                )
            )
        )

        if date_from:
            stmt = stmt.filter(self.model.order_date >= date_from)
        if date_to:
            stmt = stmt.filter(self.model.order_date <= date_to)

        result = await self.db.execute(stmt)
        total = result.scalar()
        return float(total) if total else 0.0

    async def count_by_status(
        self,
        tenant_id: UUID,
        status: PurchaseOrderStatus
    ) -> int:
        """Count purchase orders by status"""
        stmt = (
            select(func.count())
            .select_from(self.model)
            .filter(
                and_(
                    self.model.tenant_id == tenant_id,
                    self.model.status == status
                )
            )
        )
        result = await self.db.execute(stmt)
        return result.scalar() or 0
