"""
Analytics Views - Advanced Analytics & Reporting Endpoints

These endpoints provide detailed analytics data for bookings, revenue, and business metrics.
Access is gated by the 'advanced_analytics' permission from the subscription plan.
"""
from django.utils import timezone
from django.db.models import Count, Sum, Q, Avg
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from datetime import timedelta, datetime

from smoothschedule.identity.core.permissions import HasFeaturePermission
from smoothschedule.scheduling.schedule.models import Event, Service, Participant
from smoothschedule.identity.users.models import User


class AnalyticsViewSet(viewsets.ViewSet):
    """
    Analytics ViewSet providing detailed reporting and business insights.

    All endpoints require authentication and the 'advanced_analytics' permission.

    Endpoints:
    - GET /api/analytics/dashboard/ - Dashboard summary statistics
    - GET /api/analytics/appointments/ - Appointment analytics
    - GET /api/analytics/revenue/ - Revenue analytics (if payments enabled)
    """

    permission_classes = [IsAuthenticated, HasFeaturePermission('advanced_analytics')]

    @action(detail=False, methods=['get'])
    def dashboard(self, request):
        """
        Dashboard Summary Statistics

        Returns high-level metrics for the tenant's dashboard:
        - Total appointments (this month and all time)
        - Active resources and services
        - Upcoming appointments
        - Busiest times

        Returns:
            {
                "total_appointments_this_month": int,
                "total_appointments_all_time": int,
                "active_resources_count": int,
                "active_services_count": int,
                "upcoming_appointments_count": int,
                "average_appointment_duration_minutes": float,
                "peak_booking_day": str (day of week),
                "peak_booking_hour": int (0-23),
                "period": {"start_date": str, "end_date": str}
            }
        """
        now = timezone.now()
        start_of_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
        next_month = start_of_month + timedelta(days=32)
        start_of_next_month = next_month.replace(day=1)

        # Count appointments this month
        appointments_this_month = Event.objects.filter(
            start_time__gte=start_of_month,
            start_time__lt=start_of_next_month,
            status='confirmed'
        ).count()

        # Count all appointments
        appointments_all_time = Event.objects.filter(status='confirmed').count()

        # Count active resources
        active_resources = Event.objects.filter(
            start_time__gte=now,
            status='confirmed'
        ).values('resource').distinct().count()

        # Count active services
        active_services = Event.objects.filter(
            start_time__gte=now,
            status='confirmed'
        ).values('service').distinct().count()

        # Count upcoming appointments (next 7 days)
        week_from_now = now + timedelta(days=7)
        upcoming_appointments = Event.objects.filter(
            start_time__gte=now,
            start_time__lt=week_from_now,
            status='confirmed'
        ).count()

        # Calculate average appointment duration
        durations = Event.objects.filter(
            status='confirmed'
        ).values('start_time', 'end_time')

        avg_duration = 0.0
        if durations.exists():
            total_minutes = 0
            count = 0
            for event in durations:
                if event['end_time'] and event['start_time']:
                    duration = (event['end_time'] - event['start_time']).total_seconds() / 60
                    total_minutes += duration
                    count += 1
            avg_duration = round(total_minutes / count, 2) if count > 0 else 0.0

        # Find peak booking day (day of week)
        peak_day_stats = Event.objects.filter(
            status='confirmed'
        ).extra(
            select={'dow': 'EXTRACT(dow FROM start_time)'}
        ).values('dow').annotate(count=Count('id')).order_by('-count')

        days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
        peak_day = days[int(peak_day_stats[0]['dow'])] if peak_day_stats.exists() else 'Unknown'

        # Find peak booking hour
        peak_hour_stats = Event.objects.filter(
            status='confirmed'
        ).extra(
            select={'hour': 'EXTRACT(hour FROM start_time)'}
        ).values('hour').annotate(count=Count('id')).order_by('-count')

        peak_hour = int(peak_hour_stats[0]['hour']) if peak_hour_stats.exists() else 0

        return Response({
            'total_appointments_this_month': appointments_this_month,
            'total_appointments_all_time': appointments_all_time,
            'active_resources_count': active_resources,
            'active_services_count': active_services,
            'upcoming_appointments_count': upcoming_appointments,
            'average_appointment_duration_minutes': avg_duration,
            'peak_booking_day': peak_day,
            'peak_booking_hour': peak_hour,
            'period': {
                'start_date': start_of_month.isoformat(),
                'end_date': (start_of_next_month - timedelta(days=1)).isoformat()
            }
        })

    @action(detail=False, methods=['get'])
    def appointments(self, request):
        """
        Appointment Analytics

        Query Parameters:
        - days: Number of days to analyze (default: 30)
        - status: Filter by status (confirmed, cancelled, no_show)
        - service_id: Filter by service (optional)
        - resource_id: Filter by resource (optional)

        Returns appointment breakdown including:
        - Total appointments
        - Appointments by status
        - Appointments by service
        - Appointments by resource
        - Daily breakdown
        - Booking trends

        Returns:
            {
                "total": int,
                "by_status": {"confirmed": int, "cancelled": int, "no_show": int},
                "by_service": [{"service_id": int, "service_name": str, "count": int}, ...],
                "by_resource": [{"resource_id": int, "resource_name": str, "count": int}, ...],
                "daily_breakdown": [
                    {"date": str, "count": int, "status_breakdown": {...}},
                    ...
                ],
                "booking_trend": float (percentage change from previous period),
                "cancellation_rate": float (percentage),
                "no_show_rate": float (percentage)
            }
        """
        days = int(request.query_params.get('days', 30))
        status_filter = request.query_params.get('status', None)
        service_id = request.query_params.get('service_id', None)
        resource_id = request.query_params.get('resource_id', None)

        now = timezone.now()
        period_start = now - timedelta(days=days)
        previous_period_start = period_start - timedelta(days=days)

        # Build query
        query = Event.objects.filter(start_time__gte=period_start)

        if status_filter:
            query = query.filter(status=status_filter)

        if service_id:
            query = query.filter(service_id=service_id)

        if resource_id:
            query = query.filter(resource_id=resource_id)

        total = query.count()

        # Get status breakdown
        status_breakdown = {}
        for event_status in ['confirmed', 'cancelled', 'no_show']:
            status_breakdown[event_status] = query.filter(status=event_status).count()

        # Get breakdown by service
        service_breakdown = []
        for service in Service.objects.filter(event__in=query).distinct():
            count = query.filter(service=service).count()
            service_breakdown.append({
                'service_id': service.id,
                'service_name': service.name,
                'count': count
            })

        # Get breakdown by resource
        resource_breakdown = []
        for event in query.select_related('resource').distinct():
            if event.resource:
                resource_breakdown.append({
                    'resource_id': event.resource.id,
                    'resource_name': event.resource.name
                })

        # Deduplicate and count resources
        resource_counts = {}
        for event in query.select_related('resource'):
            if event.resource:
                key = event.resource.id
                if key not in resource_counts:
                    resource_counts[key] = {
                        'resource_id': key,
                        'resource_name': event.resource.name,
                        'count': 0
                    }
                resource_counts[key]['count'] += 1

        resource_breakdown = list(resource_counts.values())

        # Daily breakdown
        daily_data = {}
        for event in query:
            date_key = event.start_time.date().isoformat()
            if date_key not in daily_data:
                daily_data[date_key] = {
                    'date': date_key,
                    'count': 0,
                    'status_breakdown': {
                        'confirmed': 0,
                        'cancelled': 0,
                        'no_show': 0
                    }
                }
            daily_data[date_key]['count'] += 1
            daily_data[date_key]['status_breakdown'][event.status] += 1

        daily_breakdown = sorted(daily_data.values(), key=lambda x: x['date'])

        # Calculate booking trend (percentage change from previous period)
        previous_count = Event.objects.filter(
            start_time__gte=previous_period_start,
            start_time__lt=period_start
        ).count()

        trend = 0.0
        if previous_count > 0:
            trend = round(((total - previous_count) / previous_count) * 100, 2)

        # Calculate cancellation rate
        cancellation_rate = 0.0
        if total > 0:
            cancellation_rate = round((status_breakdown['cancelled'] / total) * 100, 2)

        # Calculate no-show rate
        no_show_rate = 0.0
        if total > 0:
            no_show_rate = round((status_breakdown['no_show'] / total) * 100, 2)

        return Response({
            'total': total,
            'by_status': status_breakdown,
            'by_service': service_breakdown,
            'by_resource': resource_breakdown,
            'daily_breakdown': daily_breakdown,
            'booking_trend_percent': trend,
            'cancellation_rate_percent': cancellation_rate,
            'no_show_rate_percent': no_show_rate,
            'period_days': days
        })

    @action(detail=False, methods=['get'])
    def revenue(self, request):
        """
        Revenue Analytics (if payments are enabled)

        Query Parameters:
        - days: Number of days to analyze (default: 30)
        - service_id: Filter by service (optional)

        Returns revenue breakdown including:
        - Total revenue
        - Revenue by service
        - Revenue by payment method
        - Daily revenue breakdown
        - Average transaction value

        Note: This endpoint requires both 'advanced_analytics' and
        'can_accept_payments' permissions.

        Returns:
            {
                "total_revenue_cents": int,
                "transaction_count": int,
                "average_transaction_value_cents": int,
                "by_service": [
                    {"service_id": int, "service_name": str, "revenue_cents": int, "count": int},
                    ...
                ],
                "daily_breakdown": [
                    {"date": str, "revenue_cents": int, "transaction_count": int},
                    ...
                ],
                "period_days": int
            }
        """
        from smoothschedule.commerce.payments.models import Payment

        # Check if tenant has payment permissions
        tenant = getattr(request, 'tenant', None)
        if not tenant or not tenant.has_feature('can_accept_payments'):
            return Response(
                {
                    'error': 'Payment analytics not available',
                    'detail': 'Your plan does not include payment processing.'
                },
                status=status.HTTP_403_FORBIDDEN
            )

        days = int(request.query_params.get('days', 30))
        service_id = request.query_params.get('service_id', None)

        now = timezone.now()
        period_start = now - timedelta(days=days)

        # Build query
        query = Payment.objects.filter(
            created_at__gte=period_start,
            status='completed'
        )

        if service_id:
            query = query.filter(service_id=service_id)

        total_revenue = query.aggregate(Sum('amount_cents'))['amount_cents__sum'] or 0
        transaction_count = query.count()
        average_transaction = int(total_revenue / transaction_count) if transaction_count > 0 else 0

        # Revenue by service
        service_revenue = []
        for payment in query.select_related('service').distinct():
            if payment.service:
                service_revenue.append({
                    'service_id': payment.service.id,
                    'service_name': payment.service.name,
                    'revenue_cents': payment.amount_cents,
                    'count': 1
                })

        # Aggregate service revenue
        service_revenue_agg = {}
        for payment in query.select_related('service'):
            if payment.service:
                key = payment.service.id
                if key not in service_revenue_agg:
                    service_revenue_agg[key] = {
                        'service_id': key,
                        'service_name': payment.service.name,
                        'revenue_cents': 0,
                        'count': 0
                    }
                service_revenue_agg[key]['revenue_cents'] += payment.amount_cents
                service_revenue_agg[key]['count'] += 1

        service_revenue = list(service_revenue_agg.values())

        # Daily breakdown
        daily_data = {}
        for payment in query:
            date_key = payment.created_at.date().isoformat()
            if date_key not in daily_data:
                daily_data[date_key] = {
                    'date': date_key,
                    'revenue_cents': 0,
                    'transaction_count': 0
                }
            daily_data[date_key]['revenue_cents'] += payment.amount_cents
            daily_data[date_key]['transaction_count'] += 1

        daily_breakdown = sorted(daily_data.values(), key=lambda x: x['date'])

        return Response({
            'total_revenue_cents': int(total_revenue),
            'transaction_count': transaction_count,
            'average_transaction_value_cents': average_transaction,
            'by_service': service_revenue,
            'daily_breakdown': daily_breakdown,
            'period_days': days
        })
