# -*- coding: utf-8 -*-
"""
Send Test Notification Management Command

This command allows sending test notifications to users for testing
the notification system functionality.

Usage:
    python manage.py send_test_notification --user <user_id> --type <type_slug>
    python manage.py send_test_notification --email <email> --type welcome
    python manage.py send_test_notification --all-users --type system_maintenance

Author: AdTlas Development Team
Version: 1.0.0
Last Updated: 2024
"""

import logging
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth import get_user_model
from django.utils import timezone
from apps.notifications.models import NotificationType
from apps.notifications.services import NotificationService

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


class Command(BaseCommand):
    """
    Management command to send test notifications.
    
    This command provides various options for sending test notifications
    to specific users, all users, or users matching certain criteria.
    """
    
    help = 'Send test notifications to users'
    
    def add_arguments(self, parser):
        """
        Add command line arguments.
        
        Args:
            parser: Argument parser instance
        """
        # User selection options
        user_group = parser.add_mutually_exclusive_group(required=True)
        user_group.add_argument(
            '--user',
            type=int,
            help='User ID to send notification to'
        )
        user_group.add_argument(
            '--email',
            type=str,
            help='User email to send notification to'
        )
        user_group.add_argument(
            '--all-users',
            action='store_true',
            help='Send to all active users'
        )
        user_group.add_argument(
            '--staff-only',
            action='store_true',
            help='Send to staff users only'
        )
        user_group.add_argument(
            '--superuser-only',
            action='store_true',
            help='Send to superusers only'
        )
        
        # Notification options
        parser.add_argument(
            '--type',
            type=str,
            required=True,
            help='Notification type slug'
        )
        parser.add_argument(
            '--title',
            type=str,
            help='Custom notification title'
        )
        parser.add_argument(
            '--message',
            type=str,
            help='Custom notification message'
        )
        parser.add_argument(
            '--action-url',
            type=str,
            help='Action URL for the notification'
        )
        parser.add_argument(
            '--expires-in',
            type=int,
            help='Expiration time in hours'
        )
        
        # Delivery options
        parser.add_argument(
            '--async',
            action='store_true',
            help='Send notifications asynchronously using Celery'
        )
        parser.add_argument(
            '--batch-size',
            type=int,
            default=50,
            help='Batch size for bulk sending (default: 50)'
        )
        parser.add_argument(
            '--dry-run',
            action='store_true',
            help='Show what would be sent without actually sending'
        )
        
        # Template options
        parser.add_argument(
            '--use-template',
            action='store_true',
            help='Use notification template for content'
        )
        parser.add_argument(
            '--template-context',
            type=str,
            help='JSON string with template context variables'
        )
    
    def handle(self, *args, **options):
        """
        Handle the command execution.
        
        Args:
            *args: Positional arguments
            **options: Command options
        """
        try:
            # Validate notification type
            notification_type = self._get_notification_type(options['type'])
            
            # Get recipients
            recipients = self._get_recipients(options)
            
            if not recipients:
                raise CommandError("No recipients found")
            
            # Prepare notification data
            notification_data = self._prepare_notification_data(options, notification_type)
            
            # Show summary
            self._show_summary(recipients, notification_data, options)
            
            if options['dry_run']:
                self.stdout.write(
                    self.style.WARNING('DRY RUN: No notifications were actually sent')
                )
                return
            
            # Send notifications
            if options['async']:
                results = self._send_async_notifications(recipients, notification_data, options)
            else:
                results = self._send_sync_notifications(recipients, notification_data, options)
            
            # Show results
            self._show_results(results)
            
        except Exception as e:
            logger.error(f"Error in send_test_notification command: {e}")
            raise CommandError(f"Command failed: {e}")
    
    def _get_notification_type(self, type_slug):
        """
        Get and validate notification type.
        
        Args:
            type_slug: Notification type slug
        
        Returns:
            NotificationType: Notification type instance
        
        Raises:
            CommandError: If notification type not found
        """
        try:
            return NotificationType.objects.get(slug=type_slug)
        except NotificationType.DoesNotExist:
            # Show available types
            available_types = NotificationType.objects.filter(is_active=True).values_list('slug', flat=True)
            raise CommandError(
                f"Notification type '{type_slug}' not found. "
                f"Available types: {', '.join(available_types)}"
            )
    
    def _get_recipients(self, options):
        """
        Get recipient users based on options.
        
        Args:
            options: Command options
        
        Returns:
            QuerySet: User queryset
        
        Raises:
            CommandError: If no users found
        """
        if options['user']:
            try:
                user = User.objects.get(id=options['user'], is_active=True)
                return [user]
            except User.DoesNotExist:
                raise CommandError(f"User with ID {options['user']} not found or inactive")
        
        elif options['email']:
            try:
                user = User.objects.get(email=options['email'], is_active=True)
                return [user]
            except User.DoesNotExist:
                raise CommandError(f"User with email {options['email']} not found or inactive")
        
        elif options['all_users']:
            return list(User.objects.filter(is_active=True))
        
        elif options['staff_only']:
            return list(User.objects.filter(is_active=True, is_staff=True))
        
        elif options['superuser_only']:
            return list(User.objects.filter(is_active=True, is_superuser=True))
        
        return []
    
    def _prepare_notification_data(self, options, notification_type):
        """
        Prepare notification data from options.
        
        Args:
            options: Command options
            notification_type: NotificationType instance
        
        Returns:
            dict: Notification data
        """
        # Default content
        title = options.get('title') or f"Test {notification_type.name} Notification"
        message = options.get('message') or f"This is a test {notification_type.name.lower()} notification sent from the management command."
        
        # Calculate expiration
        expires_at = None
        if options.get('expires_in'):
            expires_at = timezone.now() + timezone.timedelta(hours=options['expires_in'])
        
        # Parse template context
        template_context = {}
        if options.get('template_context'):
            import json
            try:
                template_context = json.loads(options['template_context'])
            except json.JSONDecodeError:
                raise CommandError("Invalid JSON in template-context")
        
        # Add default template context
        template_context.update({
            'test_notification': True,
            'sent_via_command': True,
            'sent_at': timezone.now().isoformat(),
        })
        
        return {
            'notification_type': notification_type,
            'title': title,
            'message': message,
            'action_url': options.get('action_url'),
            'expires_at': expires_at,
            'metadata': {
                'test_notification': True,
                'sent_via_command': True,
                'command_options': {
                    'type': options['type'],
                    'async': options.get('async', False),
                    'use_template': options.get('use_template', False),
                }
            },
            'template_context': template_context if options.get('use_template') else None,
        }
    
    def _show_summary(self, recipients, notification_data, options):
        """
        Show command execution summary.
        
        Args:
            recipients: List of recipient users
            notification_data: Notification data
            options: Command options
        """
        self.stdout.write(self.style.SUCCESS("\n=== Test Notification Summary ==="))
        self.stdout.write(f"Notification Type: {notification_data['notification_type'].name}")
        self.stdout.write(f"Title: {notification_data['title']}")
        self.stdout.write(f"Message: {notification_data['message'][:100]}{'...' if len(notification_data['message']) > 100 else ''}")
        
        if notification_data['action_url']:
            self.stdout.write(f"Action URL: {notification_data['action_url']}")
        
        if notification_data['expires_at']:
            self.stdout.write(f"Expires At: {notification_data['expires_at']}")
        
        self.stdout.write(f"Recipients: {len(recipients)} users")
        self.stdout.write(f"Delivery Mode: {'Async (Celery)' if options['async'] else 'Synchronous'}")
        
        if options['use_template']:
            self.stdout.write("Template: Enabled")
        
        if len(recipients) <= 10:
            self.stdout.write("\nRecipients:")
            for user in recipients:
                self.stdout.write(f"  - {user.email} (ID: {user.id})")
        else:
            self.stdout.write(f"\nFirst 5 recipients:")
            for user in recipients[:5]:
                self.stdout.write(f"  - {user.email} (ID: {user.id})")
            self.stdout.write(f"  ... and {len(recipients) - 5} more")
        
        self.stdout.write("\n" + "="*40)
    
    def _send_sync_notifications(self, recipients, notification_data, options):
        """
        Send notifications synchronously.
        
        Args:
            recipients: List of recipient users
            notification_data: Notification data
            options: Command options
        
        Returns:
            dict: Results summary
        """
        self.stdout.write("\nSending notifications synchronously...")
        
        notification_service = NotificationService()
        results = {
            'total': len(recipients),
            'sent': 0,
            'failed': 0,
            'skipped': 0,
            'errors': []
        }
        
        for i, recipient in enumerate(recipients, 1):
            try:
                self.stdout.write(f"\rSending {i}/{len(recipients)}: {recipient.email}", ending='')
                
                notification = notification_service.create_notification(
                    recipient=recipient,
                    notification_type=notification_data['notification_type'],
                    title=notification_data['title'],
                    message=notification_data['message'],
                    action_url=notification_data['action_url'],
                    expires_at=notification_data['expires_at'],
                    metadata=notification_data['metadata'],
                    send_immediately=True,
                    template_context=notification_data['template_context']
                )
                
                if notification:
                    results['sent'] += 1
                else:
                    results['skipped'] += 1
                    
            except Exception as e:
                results['failed'] += 1
                results['errors'].append({
                    'recipient': recipient.email,
                    'error': str(e)
                })
                logger.error(f"Error sending notification to {recipient.email}: {e}")
        
        self.stdout.write("\n")  # New line after progress
        return results
    
    def _send_async_notifications(self, recipients, notification_data, options):
        """
        Send notifications asynchronously using Celery.
        
        Args:
            recipients: List of recipient users
            notification_data: Notification data
            options: Command options
        
        Returns:
            dict: Results summary
        """
        self.stdout.write("\nSending notifications asynchronously...")
        
        try:
            from apps.notifications.tasks import send_bulk_notifications_task
            
            # Prepare task arguments
            task_args = {
                'recipient_ids': [user.id for user in recipients],
                'notification_type_slug': notification_data['notification_type'].slug,
                'title': notification_data['title'],
                'message': notification_data['message'],
                'action_url': notification_data['action_url'],
                'expires_at': notification_data['expires_at'].isoformat() if notification_data['expires_at'] else None,
                'metadata': notification_data['metadata'],
                'template_context': notification_data['template_context'],
                'batch_size': options['batch_size']
            }
            
            # Queue the task
            task_result = send_bulk_notifications_task.delay(**task_args)
            
            self.stdout.write(f"Task queued with ID: {task_result.id}")
            self.stdout.write("Use 'celery -A config worker' to process the task")
            
            return {
                'total': len(recipients),
                'queued': len(recipients),
                'task_id': task_result.id,
                'async': True
            }
            
        except ImportError:
            raise CommandError("Celery not available. Use --no-async or install Celery.")
        except Exception as e:
            raise CommandError(f"Error queuing async task: {e}")
    
    def _show_results(self, results):
        """
        Show command execution results.
        
        Args:
            results: Results dictionary
        """
        self.stdout.write("\n=== Results ===")
        
        if results.get('async'):
            self.stdout.write(self.style.SUCCESS(f"✓ Queued {results['queued']} notifications"))
            self.stdout.write(f"Task ID: {results['task_id']}")
            self.stdout.write("Check Celery worker logs for processing status")
        else:
            self.stdout.write(self.style.SUCCESS(f"✓ Sent: {results['sent']}"))
            
            if results['skipped'] > 0:
                self.stdout.write(self.style.WARNING(f"⚠ Skipped: {results['skipped']}"))
            
            if results['failed'] > 0:
                self.stdout.write(self.style.ERROR(f"✗ Failed: {results['failed']}"))
                
                if results['errors']:
                    self.stdout.write("\nErrors:")
                    for error in results['errors'][:5]:  # Show first 5 errors
                        self.stdout.write(f"  - {error['recipient']}: {error['error']}")
                    
                    if len(results['errors']) > 5:
                        self.stdout.write(f"  ... and {len(results['errors']) - 5} more errors")
        
        self.stdout.write("\nDone!")