# -*- coding: utf-8 -*-
"""
Business logic services for the activities app.

This module contains service classes that handle the core business logic
for activity management, tracking, and analytics.
"""

from django.contrib.auth import get_user_model
from django.db.models import Count, Q, F
from django.utils import timezone
from django.core.cache import cache
from django.conf import settings
from datetime import timedelta, datetime
from typing import Dict, List, Optional, Any
import logging

User = get_user_model()
logger = logging.getLogger(__name__)


class ActivityService:
    """
    Service for managing user activities and tracking.
    """
    
    @staticmethod
    def log_activity(user, action, content_object=None, metadata=None):
        """
        Log a user activity.
        
        Args:
            user: User instance
            action: Activity action string
            content_object: Related object (optional)
            metadata: Additional metadata dict (optional)
        
        Returns:
            Activity instance
        """
        from .models import Activity
        
        try:
            activity_data = {
                'user': user,
                'action': action,
                'timestamp': timezone.now(),
                'ip_address': getattr(user, '_current_ip', None),
                'user_agent': getattr(user, '_current_user_agent', None),
                'metadata': metadata or {}
            }
            
            if content_object:
                activity_data.update({
                    'content_type': content_object._meta.model,
                    'object_id': content_object.pk
                })
            
            activity = Activity.objects.create(**activity_data)
            
            # Update user's last activity
            User.objects.filter(pk=user.pk).update(
                last_activity=timezone.now()
            )
            
            # Clear user activity cache
            cache_key = f'user_activities_{user.pk}'
            cache.delete(cache_key)
            
            logger.info(
                f'Activity logged: {user.email} - {action}'
            )
            
            return activity
            
        except Exception as e:
            logger.error(
                f'Error logging activity for user {user.pk}: {str(e)}'
            )
            return None
    
    @staticmethod
    def get_user_activities(user, limit=50, action_filter=None):
        """
        Get user activities with caching.
        
        Args:
            user: User instance
            limit: Number of activities to return
            action_filter: Filter by specific action
        
        Returns:
            QuerySet of activities
        """
        from .models import Activity
        
        cache_key = f'user_activities_{user.pk}_{limit}_{action_filter}'
        activities = cache.get(cache_key)
        
        if activities is None:
            queryset = Activity.objects.filter(user=user)
            
            if action_filter:
                queryset = queryset.filter(action=action_filter)
            
            activities = list(
                queryset.select_related('user')
                .order_by('-timestamp')[:limit]
            )
            
            cache.set(cache_key, activities, 300)  # 5 minutes
        
        return activities
    
    @staticmethod
    def get_activity_stats(user, days=30):
        """
        Get activity statistics for a user.
        
        Args:
            user: User instance
            days: Number of days to analyze
        
        Returns:
            Dict with activity statistics
        """
        from .models import Activity
        
        cache_key = f'activity_stats_{user.pk}_{days}'
        stats = cache.get(cache_key)
        
        if stats is None:
            start_date = timezone.now() - timedelta(days=days)
            
            activities = Activity.objects.filter(
                user=user,
                timestamp__gte=start_date
            )
            
            stats = {
                'total_activities': activities.count(),
                'unique_actions': activities.values('action').distinct().count(),
                'daily_average': activities.count() / days,
                'most_common_actions': list(
                    activities.values('action')
                    .annotate(count=Count('action'))
                    .order_by('-count')[:5]
                ),
                'activity_by_day': {}
            }
            
            # Activity by day
            for i in range(days):
                day = start_date + timedelta(days=i)
                day_activities = activities.filter(
                    timestamp__date=day.date()
                ).count()
                stats['activity_by_day'][day.strftime('%Y-%m-%d')] = day_activities
            
            cache.set(cache_key, stats, 3600)  # 1 hour
        
        return stats
    
    @staticmethod
    def get_recent_activities(limit=100, action_filter=None):
        """
        Get recent activities across all users.
        
        Args:
            limit: Number of activities to return
            action_filter: Filter by specific action
        
        Returns:
            QuerySet of activities
        """
        from .models import Activity
        
        cache_key = f'recent_activities_{limit}_{action_filter}'
        activities = cache.get(cache_key)
        
        if activities is None:
            queryset = Activity.objects.all()
            
            if action_filter:
                queryset = queryset.filter(action=action_filter)
            
            activities = list(
                queryset.select_related('user')
                .order_by('-timestamp')[:limit]
            )
            
            cache.set(cache_key, activities, 60)  # 1 minute
        
        return activities


class ActivityAnalyticsService:
    """
    Service for activity analytics and reporting.
    """
    
    @staticmethod
    def get_platform_stats(days=30):
        """
        Get platform-wide activity statistics.
        
        Args:
            days: Number of days to analyze
        
        Returns:
            Dict with platform statistics
        """
        from .models import Activity
        
        cache_key = f'platform_stats_{days}'
        stats = cache.get(cache_key)
        
        if stats is None:
            start_date = timezone.now() - timedelta(days=days)
            
            activities = Activity.objects.filter(
                timestamp__gte=start_date
            )
            
            active_users = activities.values('user').distinct().count()
            total_users = User.objects.filter(is_active=True).count()
            
            stats = {
                'total_activities': activities.count(),
                'active_users': active_users,
                'total_users': total_users,
                'engagement_rate': (active_users / total_users * 100) if total_users > 0 else 0,
                'avg_activities_per_user': activities.count() / active_users if active_users > 0 else 0,
                'top_actions': list(
                    activities.values('action')
                    .annotate(count=Count('action'))
                    .order_by('-count')[:10]
                ),
                'hourly_distribution': {},
                'daily_distribution': {}
            }
            
            # Hourly distribution
            for hour in range(24):
                hour_activities = activities.filter(
                    timestamp__hour=hour
                ).count()
                stats['hourly_distribution'][hour] = hour_activities
            
            # Daily distribution
            for i in range(days):
                day = start_date + timedelta(days=i)
                day_activities = activities.filter(
                    timestamp__date=day.date()
                ).count()
                stats['daily_distribution'][day.strftime('%Y-%m-%d')] = day_activities
            
            cache.set(cache_key, stats, 1800)  # 30 minutes
        
        return stats
    
    @staticmethod
    def get_user_engagement_report(days=30):
        """
        Generate user engagement report.
        
        Args:
            days: Number of days to analyze
        
        Returns:
            Dict with engagement data
        """
        from .models import Activity
        
        cache_key = f'engagement_report_{days}'
        report = cache.get(cache_key)
        
        if report is None:
            start_date = timezone.now() - timedelta(days=days)
            
            # Get user activity counts
            user_activities = (
                Activity.objects.filter(timestamp__gte=start_date)
                .values('user__email', 'user__first_name', 'user__last_name')
                .annotate(
                    activity_count=Count('id'),
                    last_activity=F('timestamp')
                )
                .order_by('-activity_count')
            )
            
            # Categorize users by engagement level
            high_engagement = []
            medium_engagement = []
            low_engagement = []
            
            for user_data in user_activities:
                count = user_data['activity_count']
                if count >= 50:
                    high_engagement.append(user_data)
                elif count >= 10:
                    medium_engagement.append(user_data)
                else:
                    low_engagement.append(user_data)
            
            report = {
                'high_engagement': high_engagement[:20],  # Top 20
                'medium_engagement': medium_engagement[:20],
                'low_engagement': low_engagement[:20],
                'engagement_summary': {
                    'high_count': len(high_engagement),
                    'medium_count': len(medium_engagement),
                    'low_count': len(low_engagement)
                }
            }
            
            cache.set(cache_key, report, 3600)  # 1 hour
        
        return report
    
    @staticmethod
    def export_activity_data(user=None, start_date=None, end_date=None, format='csv'):
        """
        Export activity data for analysis.
        
        Args:
            user: Specific user to export (optional)
            start_date: Start date for export
            end_date: End date for export
            format: Export format ('csv', 'json')
        
        Returns:
            Exported data
        """
        from .models import Activity
        import csv
        import json
        from io import StringIO
        
        queryset = Activity.objects.all()
        
        if user:
            queryset = queryset.filter(user=user)
        
        if start_date:
            queryset = queryset.filter(timestamp__gte=start_date)
        
        if end_date:
            queryset = queryset.filter(timestamp__lte=end_date)
        
        activities = queryset.select_related('user').order_by('-timestamp')
        
        if format == 'csv':
            output = StringIO()
            writer = csv.writer(output)
            
            # Write header
            writer.writerow([
                'User Email', 'Action', 'Timestamp', 'IP Address',
                'User Agent', 'Metadata'
            ])
            
            # Write data
            for activity in activities:
                writer.writerow([
                    activity.user.email,
                    activity.action,
                    activity.timestamp.isoformat(),
                    activity.ip_address or '',
                    activity.user_agent or '',
                    json.dumps(activity.metadata) if activity.metadata else ''
                ])
            
            return output.getvalue()
        
        elif format == 'json':
            data = []
            for activity in activities:
                data.append({
                    'user_email': activity.user.email,
                    'action': activity.action,
                    'timestamp': activity.timestamp.isoformat(),
                    'ip_address': activity.ip_address,
                    'user_agent': activity.user_agent,
                    'metadata': activity.metadata
                })
            
            return json.dumps(data, indent=2)
        
        else:
            raise ValueError(f'Unsupported format: {format}')


class ActivityCleanupService:
    """
    Service for cleaning up old activity records.
    """
    
    @staticmethod
    def cleanup_old_activities(days=90, batch_size=1000):
        """
        Clean up activities older than specified days.
        
        Args:
            days: Number of days to keep
            batch_size: Number of records to delete per batch
        
        Returns:
            Number of deleted records
        """
        from .models import Activity
        
        cutoff_date = timezone.now() - timedelta(days=days)
        
        total_deleted = 0
        
        while True:
            # Get batch of old activities
            old_activities = list(
                Activity.objects.filter(timestamp__lt=cutoff_date)
                .values_list('id', flat=True)[:batch_size]
            )
            
            if not old_activities:
                break
            
            # Delete batch
            deleted_count = Activity.objects.filter(
                id__in=old_activities
            ).delete()[0]
            
            total_deleted += deleted_count
            
            logger.info(
                f'Deleted {deleted_count} old activities '
                f'(total: {total_deleted})'
            )
        
        # Clear related caches
        cache.delete_many([
            key for key in cache._cache.keys()
            if 'activity' in key or 'engagement' in key
        ])
        
        logger.info(
            f'Activity cleanup completed. Total deleted: {total_deleted}'
        )
        
        return total_deleted
    
    @staticmethod
    def archive_activities(days=365, archive_format='json'):
        """
        Archive old activities to external storage.
        
        Args:
            days: Number of days to keep in main database
            archive_format: Format for archived data
        
        Returns:
            Path to archived file
        """
        from .models import Activity
        import os
        from django.conf import settings
        
        cutoff_date = timezone.now() - timedelta(days=days)
        
        # Get activities to archive
        activities_to_archive = Activity.objects.filter(
            timestamp__lt=cutoff_date
        ).select_related('user')
        
        if not activities_to_archive.exists():
            logger.info('No activities to archive')
            return None
        
        # Create archive directory
        archive_dir = os.path.join(settings.MEDIA_ROOT, 'archives', 'activities')
        os.makedirs(archive_dir, exist_ok=True)
        
        # Generate archive filename
        archive_filename = f'activities_archive_{timezone.now().strftime("%Y%m%d_%H%M%S")}.{archive_format}'
        archive_path = os.path.join(archive_dir, archive_filename)
        
        # Export data
        exported_data = ActivityAnalyticsService.export_activity_data(
            start_date=None,
            end_date=cutoff_date,
            format=archive_format
        )
        
        # Write to file
        with open(archive_path, 'w', encoding='utf-8') as f:
            f.write(exported_data)
        
        # Delete archived activities
        deleted_count = activities_to_archive.delete()[0]
        
        logger.info(
            f'Archived {deleted_count} activities to {archive_path}'
        )
        
        return archive_path