# services/inventory/app/repositories/dashboard_repository.py
"""
Dashboard Repository for complex dashboard queries
"""

from typing import List, Optional, Dict, Any
from uuid import UUID
from datetime import datetime
from decimal import Decimal
from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession
import structlog

logger = structlog.get_logger()


class DashboardRepository:
    """Repository for dashboard-specific database queries"""

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

    async def get_business_model_metrics(self, tenant_id: UUID) -> Dict[str, Any]:
        """Get ingredient metrics for business model detection"""
        try:
            query = text("""
                SELECT
                    COUNT(*) as total_ingredients,
                    COUNT(CASE WHEN product_type::text = 'FINISHED_PRODUCT' THEN 1 END) as finished_products,
                    COUNT(CASE WHEN product_type::text = 'INGREDIENT' THEN 1 END) as raw_ingredients,
                    COUNT(DISTINCT st.supplier_id) as supplier_count,
                    AVG(CASE WHEN s.available_quantity IS NOT NULL THEN s.available_quantity ELSE 0 END) as avg_stock_level
                FROM ingredients i
                LEFT JOIN (
                    SELECT ingredient_id, SUM(available_quantity) as available_quantity
                    FROM stock WHERE tenant_id = :tenant_id GROUP BY ingredient_id
                ) s ON i.id = s.ingredient_id
                LEFT JOIN (
                    SELECT ingredient_id, supplier_id
                    FROM stock WHERE tenant_id = :tenant_id AND supplier_id IS NOT NULL
                    GROUP BY ingredient_id, supplier_id
                ) st ON i.id = st.ingredient_id
                WHERE i.tenant_id = :tenant_id AND i.is_active = true
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            row = result.fetchone()

            if not row:
                return {
                    "total_ingredients": 0,
                    "finished_products": 0,
                    "raw_ingredients": 0,
                    "supplier_count": 0,
                    "avg_stock_level": 0
                }

            return {
                "total_ingredients": row.total_ingredients,
                "finished_products": row.finished_products,
                "raw_ingredients": row.raw_ingredients,
                "supplier_count": row.supplier_count,
                "avg_stock_level": float(row.avg_stock_level) if row.avg_stock_level else 0
            }

        except Exception as e:
            logger.error("Failed to get business model metrics", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_stock_by_category(self, tenant_id: UUID) -> Dict[str, Dict[str, Any]]:
        """Get stock breakdown by category"""
        try:
            query = text("""
                SELECT
                    COALESCE(i.ingredient_category::text, i.product_category::text, 'other') as category,
                    COUNT(*) as count,
                    COALESCE(SUM(s.available_quantity * s.unit_cost), 0) as total_value
                FROM ingredients i
                LEFT JOIN (
                    SELECT ingredient_id, SUM(available_quantity) as available_quantity, AVG(unit_cost) as unit_cost
                    FROM stock WHERE tenant_id = :tenant_id GROUP BY ingredient_id
                ) s ON i.id = s.ingredient_id
                WHERE i.tenant_id = :tenant_id AND i.is_active = true
                GROUP BY category
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            categories = {}

            for row in result.fetchall():
                categories[row.category] = {
                    "count": row.count,
                    "total_value": float(row.total_value)
                }

            return categories

        except Exception as e:
            logger.error("Failed to get stock by category", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_alerts_by_severity(self, tenant_id: UUID) -> Dict[str, int]:
        """Get active alerts breakdown by severity"""
        try:
            query = text("""
                SELECT severity, COUNT(*) as count
                FROM food_safety_alerts
                WHERE tenant_id = :tenant_id AND status = 'active'
                GROUP BY severity
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            alerts = {"critical": 0, "high": 0, "medium": 0, "low": 0}

            for row in result.fetchall():
                alerts[row.severity] = row.count

            return alerts

        except Exception as e:
            logger.error("Failed to get alerts by severity", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_movements_by_type(self, tenant_id: UUID, days: int = 7) -> Dict[str, int]:
        """Get stock movements breakdown by type for recent period"""
        try:
            query = text("""
                SELECT sm.movement_type, COUNT(*) as count
                FROM stock_movements sm
                JOIN ingredients i ON sm.ingredient_id = i.id
                WHERE i.tenant_id = :tenant_id
                AND sm.movement_date > NOW() - INTERVAL '7 days'
                GROUP BY sm.movement_type
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            movements = {}

            for row in result.fetchall():
                movements[row.movement_type] = row.count

            return movements

        except Exception as e:
            logger.error("Failed to get movements by type", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_alert_trend(self, tenant_id: UUID, days: int = 30) -> List[Dict[str, Any]]:
        """Get alert trend over time"""
        try:
            query = text(f"""
                SELECT
                    DATE(created_at) as alert_date,
                    COUNT(*) as alert_count,
                    COUNT(CASE WHEN severity IN ('high', 'critical') THEN 1 END) as high_severity_count
                FROM food_safety_alerts
                WHERE tenant_id = :tenant_id
                AND created_at > NOW() - INTERVAL '{days} days'
                GROUP BY DATE(created_at)
                ORDER BY alert_date
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})

            return [
                {
                    "date": row.alert_date.isoformat(),
                    "total_alerts": row.alert_count,
                    "high_severity_alerts": row.high_severity_count
                }
                for row in result.fetchall()
            ]

        except Exception as e:
            logger.error("Failed to get alert trend", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_recent_stock_movements(
        self,
        tenant_id: UUID,
        limit: int = 20
    ) -> List[Dict[str, Any]]:
        """Get recent stock movements"""
        try:
            query = text("""
                SELECT
                    'stock_movement' as activity_type,
                    CASE
                        WHEN movement_type = 'PURCHASE' THEN 'Stock added: ' || i.name || ' (' || sm.quantity || ' ' || i.unit_of_measure::text || ')'
                        WHEN movement_type = 'PRODUCTION_USE' THEN 'Stock consumed: ' || i.name || ' (' || sm.quantity || ' ' || i.unit_of_measure::text || ')'
                        WHEN movement_type = 'WASTE' THEN 'Stock wasted: ' || i.name || ' (' || sm.quantity || ' ' || i.unit_of_measure::text || ')'
                        WHEN movement_type = 'ADJUSTMENT' THEN 'Stock adjusted: ' || i.name || ' (' || sm.quantity || ' ' || i.unit_of_measure::text || ')'
                        ELSE 'Stock movement: ' || i.name
                    END as description,
                    sm.movement_date as timestamp,
                    sm.created_by as user_id,
                    CASE
                        WHEN movement_type = 'WASTE' THEN 'high'
                        WHEN movement_type = 'ADJUSTMENT' THEN 'medium'
                        ELSE 'low'
                    END as impact_level,
                    sm.id as entity_id,
                    'stock_movement' as entity_type
                FROM stock_movements sm
                JOIN ingredients i ON sm.ingredient_id = i.id
                WHERE i.tenant_id = :tenant_id
                ORDER BY sm.movement_date DESC
                LIMIT :limit
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id, "limit": limit})

            return [
                {
                    "activity_type": row.activity_type,
                    "description": row.description,
                    "timestamp": row.timestamp,
                    "user_id": row.user_id,
                    "impact_level": row.impact_level,
                    "entity_id": row.entity_id,
                    "entity_type": row.entity_type
                }
                for row in result.fetchall()
            ]

        except Exception as e:
            logger.error("Failed to get recent stock movements", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_recent_food_safety_alerts(
        self,
        tenant_id: UUID,
        limit: int = 20
    ) -> List[Dict[str, Any]]:
        """Get recent food safety alerts"""
        try:
            query = text("""
                SELECT
                    'food_safety_alert' as activity_type,
                    title as description,
                    created_at as timestamp,
                    created_by as user_id,
                    CASE
                        WHEN severity = 'critical' THEN 'high'
                        WHEN severity = 'high' THEN 'medium'
                        ELSE 'low'
                    END as impact_level,
                    id as entity_id,
                    'food_safety_alert' as entity_type
                FROM food_safety_alerts
                WHERE tenant_id = :tenant_id
                ORDER BY created_at DESC
                LIMIT :limit
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id, "limit": limit})

            return [
                {
                    "activity_type": row.activity_type,
                    "description": row.description,
                    "timestamp": row.timestamp,
                    "user_id": row.user_id,
                    "impact_level": row.impact_level,
                    "entity_id": row.entity_id,
                    "entity_type": row.entity_type
                }
                for row in result.fetchall()
            ]

        except Exception as e:
            logger.error("Failed to get recent food safety alerts", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_live_metrics(self, tenant_id: UUID) -> Dict[str, Any]:
        """Get real-time inventory metrics"""
        try:
            query = text("""
                SELECT
                    COUNT(DISTINCT i.id) as total_ingredients,
                    COUNT(CASE WHEN s.available_quantity > i.low_stock_threshold THEN 1 END) as in_stock,
                    COUNT(CASE WHEN s.available_quantity <= i.low_stock_threshold THEN 1 END) as low_stock,
                    COUNT(CASE WHEN s.available_quantity = 0 THEN 1 END) as out_of_stock,
                    COALESCE(SUM(s.available_quantity * s.unit_cost), 0) as total_value,
                    COUNT(CASE WHEN s.expiration_date < NOW() THEN 1 END) as expired_items,
                    COUNT(CASE WHEN s.expiration_date BETWEEN NOW() AND NOW() + INTERVAL '7 days' THEN 1 END) as expiring_soon
                FROM ingredients i
                LEFT JOIN stock s ON i.id = s.ingredient_id AND s.is_available = true
                WHERE i.tenant_id = :tenant_id AND i.is_active = true
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            metrics = result.fetchone()

            if not metrics:
                return {
                    "total_ingredients": 0,
                    "in_stock": 0,
                    "low_stock": 0,
                    "out_of_stock": 0,
                    "total_value": 0.0,
                    "expired_items": 0,
                    "expiring_soon": 0,
                    "last_updated": datetime.now().isoformat()
                }

            return {
                "total_ingredients": metrics.total_ingredients,
                "in_stock": metrics.in_stock,
                "low_stock": metrics.low_stock,
                "out_of_stock": metrics.out_of_stock,
                "total_value": float(metrics.total_value),
                "expired_items": metrics.expired_items,
                "expiring_soon": metrics.expiring_soon,
                "last_updated": datetime.now().isoformat()
            }

        except Exception as e:
            logger.error("Failed to get live metrics", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_stock_status_by_category(
        self,
        tenant_id: UUID
    ) -> List[Dict[str, Any]]:
        """Get stock status breakdown by category"""
        try:
            query = text("""
                SELECT
                    COALESCE(i.ingredient_category::text, i.product_category::text, 'other') as category,
                    COUNT(DISTINCT i.id) as total_ingredients,
                    COUNT(CASE WHEN s.available_quantity > i.low_stock_threshold THEN 1 END) as in_stock,
                    COUNT(CASE WHEN s.available_quantity <= i.low_stock_threshold AND s.available_quantity > 0 THEN 1 END) as low_stock,
                    COUNT(CASE WHEN COALESCE(s.available_quantity, 0) = 0 THEN 1 END) as out_of_stock,
                    COALESCE(SUM(s.available_quantity * s.unit_cost), 0) as total_value
                FROM ingredients i
                LEFT JOIN (
                    SELECT
                        ingredient_id,
                        SUM(available_quantity) as available_quantity,
                        AVG(unit_cost) as unit_cost
                    FROM stock
                    WHERE tenant_id = :tenant_id AND is_available = true
                    GROUP BY ingredient_id
                ) s ON i.id = s.ingredient_id
                WHERE i.tenant_id = :tenant_id AND i.is_active = true
                GROUP BY category
                ORDER BY total_value DESC
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})

            return [
                {
                    "category": row.category,
                    "total_ingredients": row.total_ingredients,
                    "in_stock": row.in_stock,
                    "low_stock": row.low_stock,
                    "out_of_stock": row.out_of_stock,
                    "total_value": float(row.total_value)
                }
                for row in result.fetchall()
            ]

        except Exception as e:
            logger.error("Failed to get stock status by category", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_alerts_summary(
        self,
        tenant_id: UUID,
        alert_types: Optional[List[str]] = None,
        severities: Optional[List[str]] = None,
        date_from: Optional[datetime] = None,
        date_to: Optional[datetime] = None
    ) -> List[Dict[str, Any]]:
        """Get alerts summary by type and severity with filters"""
        try:
            # Build query with filters
            where_conditions = ["tenant_id = :tenant_id", "status = 'active'"]
            params = {"tenant_id": tenant_id}

            if alert_types:
                where_conditions.append("alert_type = ANY(:alert_types)")
                params["alert_types"] = alert_types

            if severities:
                where_conditions.append("severity = ANY(:severities)")
                params["severities"] = severities

            if date_from:
                where_conditions.append("created_at >= :date_from")
                params["date_from"] = date_from

            if date_to:
                where_conditions.append("created_at <= :date_to")
                params["date_to"] = date_to

            where_clause = " AND ".join(where_conditions)

            query = text(f"""
                SELECT
                    alert_type,
                    severity,
                    COUNT(*) as count,
                    MIN(EXTRACT(EPOCH FROM (NOW() - created_at))/3600)::int as oldest_alert_age_hours,
                    AVG(CASE WHEN resolved_at IS NOT NULL
                        THEN EXTRACT(EPOCH FROM (resolved_at - created_at))/3600
                        ELSE NULL END)::int as avg_resolution_hours
                FROM food_safety_alerts
                WHERE {where_clause}
                GROUP BY alert_type, severity
                ORDER BY severity DESC, count DESC
            """)

            result = await self.session.execute(query, params)

            return [
                {
                    "alert_type": row.alert_type,
                    "severity": row.severity,
                    "count": row.count,
                    "oldest_alert_age_hours": row.oldest_alert_age_hours,
                    "average_resolution_time_hours": row.avg_resolution_hours
                }
                for row in result.fetchall()
            ]

        except Exception as e:
            logger.error("Failed to get alerts summary", error=str(e), tenant_id=str(tenant_id))
            raise

    async def get_ingredient_stock_levels(self, tenant_id: UUID) -> Dict[str, float]:
        """
        Get current stock levels for all ingredients

        Args:
            tenant_id: Tenant UUID

        Returns:
            Dictionary mapping ingredient_id to current stock level
        """
        try:
            stock_query = text("""
                SELECT
                    i.id as ingredient_id,
                    COALESCE(SUM(s.available_quantity), 0) as current_stock
                FROM ingredients i
                LEFT JOIN stock s ON i.id = s.ingredient_id AND s.is_available = true
                WHERE i.tenant_id = :tenant_id AND i.is_active = true
                GROUP BY i.id
            """)

            result = await self.session.execute(stock_query, {"tenant_id": tenant_id})
            stock_levels = {}

            for row in result.fetchall():
                stock_levels[str(row.ingredient_id)] = float(row.current_stock)

            return stock_levels

        except Exception as e:
            logger.error("Failed to get ingredient stock levels", error=str(e), tenant_id=str(tenant_id))
            raise
