# services/suppliers/app/repositories/supplier_performance_repository.py
"""
Supplier Performance Repository - Calculate and manage supplier trust scores
Handles supplier performance metrics, trust scores, and auto-approval eligibility
"""

from typing import Optional, Dict, Any
from uuid import UUID
from datetime import datetime, timedelta, timezone
from decimal import Decimal
import structlog

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func, and_, case
from sqlalchemy.orm import selectinload

from app.models.suppliers import (
    Supplier,
    PurchaseOrder,
    PurchaseOrderStatus
)

logger = structlog.get_logger()


class SupplierPerformanceRepository:
    """Repository for calculating and managing supplier performance metrics"""

    def __init__(self, db: AsyncSession):
        self.db = db

    async def calculate_trust_score(self, supplier_id: UUID) -> float:
        """
        Calculate comprehensive trust score for a supplier

        Score components (weighted):
        - Quality rating: 30%
        - Delivery rating: 30%
        - On-time delivery rate: 20%
        - Fulfillment rate: 15%
        - Order history: 5%

        Returns:
            float: Trust score between 0.0 and 1.0
        """
        try:
            # Get supplier with current metrics
            stmt = select(Supplier).where(Supplier.id == supplier_id)
            result = await self.db.execute(stmt)
            supplier = result.scalar_one_or_none()

            if not supplier:
                logger.warning("Supplier not found for trust score calculation", supplier_id=str(supplier_id))
                return 0.0

            # Calculate on-time delivery rate from recent POs
            on_time_rate = await self._calculate_on_time_delivery_rate(supplier_id)

            # Calculate fulfillment rate from recent POs
            fulfillment_rate = await self._calculate_fulfillment_rate(supplier_id)

            # Calculate order history score (more orders = higher confidence)
            order_history_score = min(1.0, supplier.total_pos_count / 50.0)

            # Weighted components
            quality_score = (supplier.quality_rating or 0.0) / 5.0  # Normalize to 0-1
            delivery_score = (supplier.delivery_rating or 0.0) / 5.0  # Normalize to 0-1

            trust_score = (
                quality_score * 0.30 +
                delivery_score * 0.30 +
                on_time_rate * 0.20 +
                fulfillment_rate * 0.15 +
                order_history_score * 0.05
            )

            # Ensure score is between 0 and 1
            trust_score = max(0.0, min(1.0, trust_score))

            logger.info(
                "Trust score calculated",
                supplier_id=str(supplier_id),
                trust_score=trust_score,
                quality_score=quality_score,
                delivery_score=delivery_score,
                on_time_rate=on_time_rate,
                fulfillment_rate=fulfillment_rate,
                order_history_score=order_history_score
            )

            return trust_score

        except Exception as e:
            logger.error("Error calculating trust score", supplier_id=str(supplier_id), error=str(e))
            return 0.0

    async def _calculate_on_time_delivery_rate(self, supplier_id: UUID, days: int = 90) -> float:
        """Calculate percentage of orders delivered on time in the last N days"""
        try:
            cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)

            # Get completed orders with delivery dates
            stmt = select(
                func.count(PurchaseOrder.id).label('total_orders'),
                func.count(
                    case(
                        (PurchaseOrder.actual_delivery_date <= PurchaseOrder.required_delivery_date, 1)
                    )
                ).label('on_time_orders')
            ).where(
                and_(
                    PurchaseOrder.supplier_id == supplier_id,
                    PurchaseOrder.status == PurchaseOrderStatus.completed,
                    PurchaseOrder.actual_delivery_date.isnot(None),
                    PurchaseOrder.created_at >= cutoff_date
                )
            )

            result = await self.db.execute(stmt)
            row = result.one()

            if row.total_orders == 0:
                return 0.0

            on_time_rate = float(row.on_time_orders) / float(row.total_orders)
            return on_time_rate

        except Exception as e:
            logger.error("Error calculating on-time delivery rate", supplier_id=str(supplier_id), error=str(e))
            return 0.0

    async def _calculate_fulfillment_rate(self, supplier_id: UUID, days: int = 90) -> float:
        """Calculate percentage of orders fully fulfilled (no shortages) in the last N days"""
        try:
            cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)

            # Get completed/confirmed orders
            stmt = select(
                func.count(PurchaseOrder.id).label('total_orders'),
                func.count(
                    case(
                        (PurchaseOrder.status == PurchaseOrderStatus.completed, 1)
                    )
                ).label('completed_orders')
            ).where(
                and_(
                    PurchaseOrder.supplier_id == supplier_id,
                    PurchaseOrder.status.in_([
                        PurchaseOrderStatus.completed,
                        PurchaseOrderStatus.partially_received
                    ]),
                    PurchaseOrder.created_at >= cutoff_date
                )
            )

            result = await self.db.execute(stmt)
            row = result.one()

            if row.total_orders == 0:
                return 0.0

            fulfillment_rate = float(row.completed_orders) / float(row.total_orders)
            return fulfillment_rate

        except Exception as e:
            logger.error("Error calculating fulfillment rate", supplier_id=str(supplier_id), error=str(e))
            return 0.0

    async def update_supplier_performance_metrics(self, supplier_id: UUID) -> Dict[str, Any]:
        """
        Update all performance metrics for a supplier

        Returns:
            Dict with updated metrics
        """
        try:
            # Calculate all metrics
            trust_score = await self.calculate_trust_score(supplier_id)
            on_time_rate = await self._calculate_on_time_delivery_rate(supplier_id)
            fulfillment_rate = await self._calculate_fulfillment_rate(supplier_id)

            # Get current supplier
            stmt = select(Supplier).where(Supplier.id == supplier_id)
            result = await self.db.execute(stmt)
            supplier = result.scalar_one_or_none()

            if not supplier:
                return {}

            # Update supplier metrics
            supplier.trust_score = trust_score
            supplier.on_time_delivery_rate = on_time_rate
            supplier.fulfillment_rate = fulfillment_rate
            supplier.last_performance_update = datetime.now(timezone.utc)

            # Auto-update preferred status based on performance
            supplier.is_preferred_supplier = (
                supplier.total_pos_count >= 10 and
                trust_score >= 0.80 and
                supplier.status.value == 'active'
            )

            # Auto-update auto-approve eligibility
            supplier.auto_approve_enabled = (
                supplier.total_pos_count >= 20 and
                trust_score >= 0.85 and
                on_time_rate >= 0.90 and
                supplier.is_preferred_supplier and
                supplier.status.value == 'active'
            )

            await self.db.commit()

            logger.info(
                "Supplier performance metrics updated",
                supplier_id=str(supplier_id),
                trust_score=trust_score,
                is_preferred=supplier.is_preferred_supplier,
                auto_approve_enabled=supplier.auto_approve_enabled
            )

            return {
                "supplier_id": str(supplier_id),
                "trust_score": trust_score,
                "on_time_delivery_rate": on_time_rate,
                "fulfillment_rate": fulfillment_rate,
                "is_preferred_supplier": supplier.is_preferred_supplier,
                "auto_approve_enabled": supplier.auto_approve_enabled,
                "last_updated": supplier.last_performance_update.isoformat()
            }

        except Exception as e:
            await self.db.rollback()
            logger.error("Error updating supplier performance metrics", supplier_id=str(supplier_id), error=str(e))
            raise

    async def increment_po_counters(self, supplier_id: UUID, approved: bool = False):
        """Increment PO counters when a new PO is created or approved"""
        try:
            stmt = select(Supplier).where(Supplier.id == supplier_id)
            result = await self.db.execute(stmt)
            supplier = result.scalar_one_or_none()

            if supplier:
                supplier.total_pos_count += 1
                if approved:
                    supplier.approved_pos_count += 1

                await self.db.commit()
                logger.info(
                    "Supplier PO counters incremented",
                    supplier_id=str(supplier_id),
                    total=supplier.total_pos_count,
                    approved=supplier.approved_pos_count
                )

        except Exception as e:
            await self.db.rollback()
            logger.error("Error incrementing PO counters", supplier_id=str(supplier_id), error=str(e))

    async def get_supplier_with_performance(self, supplier_id: UUID) -> Optional[Dict[str, Any]]:
        """Get supplier data with all performance metrics"""
        try:
            stmt = select(Supplier).where(Supplier.id == supplier_id)
            result = await self.db.execute(stmt)
            supplier = result.scalar_one_or_none()

            if not supplier:
                return None

            return {
                "id": str(supplier.id),
                "name": supplier.name,
                "trust_score": supplier.trust_score,
                "is_preferred_supplier": supplier.is_preferred_supplier,
                "auto_approve_enabled": supplier.auto_approve_enabled,
                "total_pos_count": supplier.total_pos_count,
                "approved_pos_count": supplier.approved_pos_count,
                "on_time_delivery_rate": supplier.on_time_delivery_rate,
                "fulfillment_rate": supplier.fulfillment_rate,
                "quality_rating": supplier.quality_rating,
                "delivery_rating": supplier.delivery_rating,
                "status": supplier.status.value if supplier.status else None,
                "last_performance_update": supplier.last_performance_update.isoformat() if supplier.last_performance_update else None
            }

        except Exception as e:
            logger.error("Error getting supplier with performance", supplier_id=str(supplier_id), error=str(e))
            return None
