# services/inventory/app/repositories/inventory_alert_repository.py
"""
Inventory Alert Repository
Data access layer for inventory alert detection and analysis
"""

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

logger = structlog.get_logger()


class InventoryAlertRepository:
    """Repository for inventory alert data access"""

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

    async def get_stock_issues(self, tenant_id: UUID) -> List[Dict[str, Any]]:
        """
        Get stock level issues with CTE analysis
        Returns list of critical, low, and overstock situations
        """
        try:
            query = text("""
                WITH stock_analysis AS (
                    SELECT
                        i.id, i.name, i.tenant_id,
                        COALESCE(SUM(s.current_quantity), 0) as current_stock,
                        i.low_stock_threshold as minimum_stock,
                        i.max_stock_level as maximum_stock,
                        i.reorder_point,
                        0 as tomorrow_needed,
                        0 as avg_daily_usage,
                        7 as lead_time_days,
                        CASE
                            WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold THEN 'critical'
                            WHEN COALESCE(SUM(s.current_quantity), 0) < i.low_stock_threshold * 1.2 THEN 'low'
                            WHEN i.max_stock_level IS NOT NULL AND COALESCE(SUM(s.current_quantity), 0) > i.max_stock_level THEN 'overstock'
                            ELSE 'normal'
                        END as status,
                        GREATEST(0, i.low_stock_threshold - COALESCE(SUM(s.current_quantity), 0)) as shortage_amount
                    FROM ingredients i
                    LEFT JOIN stock s ON s.ingredient_id = i.id AND s.is_available = true
                    WHERE i.tenant_id = :tenant_id AND i.is_active = true
                    GROUP BY i.id, i.name, i.tenant_id, i.low_stock_threshold, i.max_stock_level, i.reorder_point
                )
                SELECT * FROM stock_analysis WHERE status != 'normal'
                ORDER BY
                    CASE status
                        WHEN 'critical' THEN 1
                        WHEN 'low' THEN 2
                        WHEN 'overstock' THEN 3
                    END,
                    shortage_amount DESC
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            return [dict(row._mapping) for row in result.fetchall()]

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

    async def get_expiring_products(self, tenant_id: UUID, days_threshold: int = 7) -> List[Dict[str, Any]]:
        """
        Get products expiring soon or already expired
        """
        try:
            query = text("""
                SELECT
                    i.id as ingredient_id,
                    i.name as ingredient_name,
                    s.id as stock_id,
                    s.batch_number,
                    s.expiration_date,
                    s.current_quantity,
                    i.unit_of_measure,
                    s.unit_cost,
                    (s.current_quantity * s.unit_cost) as total_value,
                    CASE
                        WHEN s.expiration_date < CURRENT_DATE THEN 'expired'
                        WHEN s.expiration_date <= CURRENT_DATE + INTERVAL '1 day' THEN 'expires_today'
                        WHEN s.expiration_date <= CURRENT_DATE + INTERVAL '3 days' THEN 'expires_soon'
                        ELSE 'warning'
                    END as urgency,
                    EXTRACT(DAY FROM (s.expiration_date - CURRENT_DATE)) as days_until_expiry
                FROM stock s
                JOIN ingredients i ON s.ingredient_id = i.id
                WHERE i.tenant_id = :tenant_id
                AND s.is_available = true
                AND s.expiration_date <= CURRENT_DATE + INTERVAL ':days_threshold days'
                ORDER BY s.expiration_date ASC, total_value DESC
            """)

            result = await self.session.execute(query, {
                "tenant_id": tenant_id,
                "days_threshold": days_threshold
            })
            return [dict(row._mapping) for row in result.fetchall()]

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

    async def get_temperature_breaches(self, tenant_id: UUID, hours_back: int = 24) -> List[Dict[str, Any]]:
        """
        Get temperature monitoring breaches
        """
        try:
            query = text("""
                SELECT
                    tl.id,
                    tl.equipment_id,
                    tl.equipment_name,
                    tl.storage_type,
                    tl.temperature_celsius,
                    tl.min_threshold,
                    tl.max_threshold,
                    tl.is_within_range,
                    tl.recorded_at,
                    tl.alert_triggered,
                    EXTRACT(EPOCH FROM (NOW() - tl.recorded_at))/3600 as hours_ago,
                    CASE
                        WHEN tl.temperature_celsius < tl.min_threshold
                        THEN tl.min_threshold - tl.temperature_celsius
                        WHEN tl.temperature_celsius > tl.max_threshold
                        THEN tl.temperature_celsius - tl.max_threshold
                        ELSE 0
                    END as deviation
                FROM temperature_logs tl
                WHERE tl.tenant_id = :tenant_id
                AND tl.is_within_range = false
                AND tl.recorded_at > NOW() - INTERVAL ':hours_back hours'
                AND tl.alert_triggered = false
                ORDER BY deviation DESC, tl.recorded_at DESC
            """)

            result = await self.session.execute(query, {
                "tenant_id": tenant_id,
                "hours_back": hours_back
            })
            return [dict(row._mapping) for row in result.fetchall()]

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

    async def mark_temperature_alert_triggered(self, log_id: UUID) -> None:
        """
        Mark a temperature log as having triggered an alert
        """
        try:
            query = text("""
                UPDATE temperature_logs
                SET alert_triggered = true
                WHERE id = :id
            """)

            await self.session.execute(query, {"id": log_id})
            await self.session.commit()

        except Exception as e:
            logger.error("Failed to mark temperature alert", error=str(e), log_id=str(log_id))
            raise

    async def get_waste_opportunities(self, tenant_id: UUID) -> List[Dict[str, Any]]:
        """
        Identify waste reduction opportunities
        """
        try:
            query = text("""
                WITH waste_analysis AS (
                    SELECT
                        i.id as ingredient_id,
                        i.name as ingredient_name,
                        i.ingredient_category,
                        COUNT(sm.id) as waste_incidents,
                        SUM(sm.quantity) as total_waste_quantity,
                        SUM(sm.total_cost) as total_waste_cost,
                        AVG(sm.quantity) as avg_waste_per_incident,
                        MAX(sm.movement_date) as last_waste_date
                    FROM stock_movements sm
                    JOIN ingredients i ON sm.ingredient_id = i.id
                    WHERE i.tenant_id = :tenant_id
                    AND sm.movement_type = 'WASTE'
                    AND sm.movement_date > NOW() - INTERVAL '30 days'
                    GROUP BY i.id, i.name, i.ingredient_category
                    HAVING COUNT(sm.id) >= 3 OR SUM(sm.total_cost) > 50
                )
                SELECT * FROM waste_analysis
                ORDER BY total_waste_cost DESC, waste_incidents DESC
                LIMIT 20
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            return [dict(row._mapping) for row in result.fetchall()]

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

    async def get_reorder_recommendations(self, tenant_id: UUID) -> List[Dict[str, Any]]:
        """
        Get ingredients that need reordering based on stock levels and usage
        """
        try:
            query = text("""
                WITH usage_analysis AS (
                    SELECT
                        i.id,
                        i.name,
                        COALESCE(SUM(s.current_quantity), 0) as current_stock,
                        i.reorder_point,
                        i.low_stock_threshold,
                        COALESCE(SUM(sm.quantity) FILTER (WHERE sm.movement_date > NOW() - INTERVAL '7 days'), 0) / 7 as daily_usage,
                        i.preferred_supplier_id,
                        i.standard_order_quantity
                    FROM ingredients i
                    LEFT JOIN stock s ON s.ingredient_id = i.id AND s.is_available = true
                    LEFT JOIN stock_movements sm ON sm.ingredient_id = i.id
                        AND sm.movement_type = 'PRODUCTION_USE'
                        AND sm.movement_date > NOW() - INTERVAL '7 days'
                    WHERE i.tenant_id = :tenant_id
                    AND i.is_active = true
                    GROUP BY i.id, i.name, i.reorder_point, i.low_stock_threshold,
                             i.preferred_supplier_id, i.standard_order_quantity
                )
                SELECT *,
                    CASE
                        WHEN daily_usage > 0 THEN FLOOR(current_stock / NULLIF(daily_usage, 0))
                        ELSE 999
                    END as days_of_stock,
                    GREATEST(
                        standard_order_quantity,
                        CEIL(daily_usage * 14)
                    ) as recommended_order_quantity
                FROM usage_analysis
                WHERE current_stock <= reorder_point
                ORDER BY days_of_stock ASC, current_stock ASC
                LIMIT 50
            """)

            result = await self.session.execute(query, {"tenant_id": tenant_id})
            return [dict(row._mapping) for row in result.fetchall()]

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

    async def get_active_tenant_ids(self) -> List[UUID]:
        """
        Get list of active tenant IDs from ingredients table
        """
        try:
            query = text("SELECT DISTINCT tenant_id FROM ingredients WHERE is_active = true")
            result = await self.session.execute(query)

            tenant_ids = []
            for row in result.fetchall():
                tenant_id = row.tenant_id
                # Convert to UUID if it's not already
                if isinstance(tenant_id, UUID):
                    tenant_ids.append(tenant_id)
                else:
                    tenant_ids.append(UUID(str(tenant_id)))
            return tenant_ids

        except Exception as e:
            logger.error("Failed to get active tenant IDs", error=str(e))
            raise

    async def get_stock_after_order(self, ingredient_id: str, order_quantity: float) -> Dict[str, Any]:
        """
        Get stock information after hypothetical order
        """
        try:
            query = text("""
                SELECT i.id, i.name,
                       COALESCE(SUM(s.current_quantity), 0) as current_stock,
                       i.low_stock_threshold as minimum_stock,
                       (COALESCE(SUM(s.current_quantity), 0) - :order_quantity) as remaining
                FROM ingredients i
                LEFT JOIN stock s ON s.ingredient_id = i.id AND s.is_available = true
                WHERE i.id = :ingredient_id
                GROUP BY i.id, i.name, i.low_stock_threshold
            """)

            result = await self.session.execute(query, {
                "ingredient_id": ingredient_id,
                "order_quantity": order_quantity
            })
            row = result.fetchone()
            return dict(row._mapping) if row else None

        except Exception as e:
            logger.error("Failed to get stock after order", error=str(e), ingredient_id=ingredient_id)
            raise
