"""
Alert Analytics Repository
Handles all database operations for alert analytics
"""

from typing import List, Dict, Any, Optional
from datetime import datetime, timedelta
from uuid import UUID
from sqlalchemy import select, func, and_, extract, case
from sqlalchemy.ext.asyncio import AsyncSession
import structlog

from app.models.alerts import Alert, AlertInteraction, AlertSeverity, AlertStatus

logger = structlog.get_logger()


class AlertAnalyticsRepository:
    """Repository for alert analytics operations"""

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

    async def create_interaction(
        self,
        tenant_id: UUID,
        alert_id: UUID,
        user_id: UUID,
        interaction_type: str,
        metadata: Optional[Dict[str, Any]] = None
    ) -> AlertInteraction:
        """Create a new alert interaction"""

        # Get alert to calculate response time
        alert_query = select(Alert).where(Alert.id == alert_id)
        result = await self.session.execute(alert_query)
        alert = result.scalar_one_or_none()

        if not alert:
            raise ValueError(f"Alert {alert_id} not found")

        # Calculate response time
        now = datetime.utcnow()
        response_time_seconds = int((now - alert.created_at).total_seconds())

        # Create interaction
        interaction = AlertInteraction(
            tenant_id=tenant_id,
            alert_id=alert_id,
            user_id=user_id,
            interaction_type=interaction_type,
            interacted_at=now,
            response_time_seconds=response_time_seconds,
            interaction_metadata=metadata or {}
        )

        self.session.add(interaction)

        # Update alert status if applicable
        if interaction_type == 'acknowledged' and alert.status == AlertStatus.ACTIVE:
            alert.status = AlertStatus.ACKNOWLEDGED
        elif interaction_type == 'resolved':
            alert.status = AlertStatus.RESOLVED
            alert.resolved_at = now
        elif interaction_type == 'dismissed':
            alert.status = AlertStatus.IGNORED

        await self.session.commit()
        await self.session.refresh(interaction)

        logger.info(
            "Alert interaction created",
            alert_id=str(alert_id),
            interaction_type=interaction_type,
            response_time=response_time_seconds
        )

        return interaction

    async def create_interactions_batch(
        self,
        tenant_id: UUID,
        interactions: List[Dict[str, Any]]
    ) -> List[AlertInteraction]:
        """Create multiple interactions in batch"""
        created_interactions = []

        for interaction_data in interactions:
            try:
                interaction = await self.create_interaction(
                    tenant_id=tenant_id,
                    alert_id=UUID(interaction_data['alert_id']),
                    user_id=UUID(interaction_data['user_id']),
                    interaction_type=interaction_data['interaction_type'],
                    metadata=interaction_data.get('metadata')
                )
                created_interactions.append(interaction)
            except Exception as e:
                logger.error(
                    "Failed to create interaction in batch",
                    error=str(e),
                    alert_id=interaction_data.get('alert_id')
                )
                continue

        return created_interactions

    async def get_analytics_trends(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> List[Dict[str, Any]]:
        """Get alert trends for the last N days"""
        start_date = datetime.utcnow() - timedelta(days=days)

        # Query alerts grouped by date and severity
        query = (
            select(
                func.date(Alert.created_at).label('date'),
                func.count(Alert.id).label('total_count'),
                func.sum(
                    case((Alert.severity == AlertSeverity.URGENT, 1), else_=0)
                ).label('urgent_count'),
                func.sum(
                    case((Alert.severity == AlertSeverity.HIGH, 1), else_=0)
                ).label('high_count'),
                func.sum(
                    case((Alert.severity == AlertSeverity.MEDIUM, 1), else_=0)
                ).label('medium_count'),
                func.sum(
                    case((Alert.severity == AlertSeverity.LOW, 1), else_=0)
                ).label('low_count')
            )
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date
                )
            )
            .group_by(func.date(Alert.created_at))
            .order_by(func.date(Alert.created_at))
        )

        result = await self.session.execute(query)
        rows = result.all()

        # Fill in missing dates with zeros
        trends = []
        current_date = start_date.date()
        end_date = datetime.utcnow().date()

        # Create a dict for quick lookup
        data_by_date = {row.date: row for row in rows}

        while current_date <= end_date:
            date_str = current_date.isoformat()
            row = data_by_date.get(current_date)

            trends.append({
                'date': date_str,
                'count': int(row.total_count) if row else 0,
                'urgentCount': int(row.urgent_count) if row else 0,
                'highCount': int(row.high_count) if row else 0,
                'mediumCount': int(row.medium_count) if row else 0,
                'lowCount': int(row.low_count) if row else 0,
            })

            current_date += timedelta(days=1)

        return trends

    async def get_average_response_time(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> int:
        """Get average response time in minutes for acknowledged alerts"""
        start_date = datetime.utcnow() - timedelta(days=days)

        query = (
            select(func.avg(AlertInteraction.response_time_seconds))
            .where(
                and_(
                    AlertInteraction.tenant_id == tenant_id,
                    AlertInteraction.interaction_type == 'acknowledged',
                    AlertInteraction.interacted_at >= start_date,
                    AlertInteraction.response_time_seconds < 86400  # Less than 24 hours
                )
            )
        )

        result = await self.session.execute(query)
        avg_seconds = result.scalar_one_or_none()

        if avg_seconds is None:
            return 0

        # Convert to minutes
        return round(avg_seconds / 60)

    async def get_top_categories(
        self,
        tenant_id: UUID,
        days: int = 7,
        limit: int = 3
    ) -> List[Dict[str, Any]]:
        """Get top alert categories"""
        start_date = datetime.utcnow() - timedelta(days=days)

        query = (
            select(
                Alert.alert_type,
                func.count(Alert.id).label('count')
            )
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date
                )
            )
            .group_by(Alert.alert_type)
            .order_by(func.count(Alert.id).desc())
            .limit(limit)
        )

        result = await self.session.execute(query)
        rows = result.all()

        # Calculate total for percentages
        total_query = (
            select(func.count(Alert.id))
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date
                )
            )
        )
        total_result = await self.session.execute(total_query)
        total = total_result.scalar_one() or 1

        categories = []
        for row in rows:
            percentage = round((row.count / total) * 100) if total > 0 else 0
            categories.append({
                'category': row.alert_type,
                'count': row.count,
                'percentage': percentage
            })

        return categories

    async def get_resolution_stats(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> Dict[str, Any]:
        """Get resolution statistics"""
        start_date = datetime.utcnow() - timedelta(days=days)

        # Total alerts
        total_query = (
            select(func.count(Alert.id))
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date
                )
            )
        )
        total_result = await self.session.execute(total_query)
        total_alerts = total_result.scalar_one() or 0

        # Resolved alerts
        resolved_query = (
            select(func.count(Alert.id))
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date,
                    Alert.status == AlertStatus.RESOLVED
                )
            )
        )
        resolved_result = await self.session.execute(resolved_query)
        resolved_alerts = resolved_result.scalar_one() or 0

        # Active alerts
        active_query = (
            select(func.count(Alert.id))
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date,
                    Alert.status == AlertStatus.ACTIVE
                )
            )
        )
        active_result = await self.session.execute(active_query)
        active_alerts = active_result.scalar_one() or 0

        resolution_rate = round((resolved_alerts / total_alerts) * 100) if total_alerts > 0 else 0

        return {
            'totalAlerts': total_alerts,
            'resolvedAlerts': resolved_alerts,
            'activeAlerts': active_alerts,
            'resolutionRate': resolution_rate
        }

    async def get_busiest_day(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> str:
        """Get busiest day of week"""
        start_date = datetime.utcnow() - timedelta(days=days)

        query = (
            select(
                extract('dow', Alert.created_at).label('day_of_week'),
                func.count(Alert.id).label('count')
            )
            .where(
                and_(
                    Alert.tenant_id == tenant_id,
                    Alert.created_at >= start_date
                )
            )
            .group_by(extract('dow', Alert.created_at))
            .order_by(func.count(Alert.id).desc())
            .limit(1)
        )

        result = await self.session.execute(query)
        row = result.first()

        if not row:
            return 'N/A'

        day_names = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
        return day_names[int(row.day_of_week)]

    async def get_predicted_daily_average(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> int:
        """Calculate predicted daily average based on trends"""
        trends = await self.get_analytics_trends(tenant_id, days)

        if not trends:
            return 0

        total_count = sum(trend['count'] for trend in trends)
        return round(total_count / len(trends))

    async def get_full_analytics(
        self,
        tenant_id: UUID,
        days: int = 7
    ) -> Dict[str, Any]:
        """Get complete analytics data"""

        trends = await self.get_analytics_trends(tenant_id, days)
        avg_response_time = await self.get_average_response_time(tenant_id, days)
        top_categories = await self.get_top_categories(tenant_id, days)
        resolution_stats = await self.get_resolution_stats(tenant_id, days)
        busiest_day = await self.get_busiest_day(tenant_id, days)
        predicted_avg = await self.get_predicted_daily_average(tenant_id, days)

        return {
            'trends': trends,
            'averageResponseTime': avg_response_time,
            'topCategories': top_categories,
            'totalAlerts': resolution_stats['totalAlerts'],
            'resolvedAlerts': resolution_stats['resolvedAlerts'],
            'activeAlerts': resolution_stats['activeAlerts'],
            'resolutionRate': resolution_stats['resolutionRate'],
            'predictedDailyAverage': predicted_avg,
            'busiestDay': busiest_day
        }
