"""
Celery tasks for portfolio snapshot generation and management

These tasks handle automated portfolio snapshot generation, health score calculations,
and performance alert notifications.
"""

from celery import shared_task
from django.utils import timezone
from django.core.mail import send_mail
from django.conf import settings
from datetime import date, timedelta
import logging

from .portfolio_snapshot_service import PortfolioSnapshotService
from .models import CustomUser
from .enhanced_permissions_models import PortfolioSnapshot

logger = logging.getLogger(__name__)


@shared_task(bind=True, max_retries=3)
def generate_daily_portfolio_snapshots(self, target_date_str=None):
    """
    Celery task to generate daily portfolio snapshots for all portfolio managers
    
    Args:
        target_date_str: Date string in YYYY-MM-DD format (optional, defaults to today)
        
    Returns:
        Dictionary with task results
    """
    try:
        # Parse target date
        if target_date_str:
            target_date = timezone.datetime.strptime(target_date_str, '%Y-%m-%d').date()
        else:
            target_date = timezone.now().date()
        
        logger.info(f"Starting daily portfolio snapshot generation for {target_date}")
        
        # Initialize service and generate snapshots
        snapshot_service = PortfolioSnapshotService()
        results = snapshot_service.generate_daily_snapshots(target_date)
        
        if results['success']:
            logger.info(f"Successfully generated snapshots: {results['snapshots_generated']} created, "
                       f"{results['snapshots_updated']} updated")
            
            # Send summary email to administrators
            send_snapshot_summary_email.delay(results)
            
            return {
                'status': 'success',
                'date': target_date.isoformat(),
                'snapshots_generated': results['snapshots_generated'],
                'snapshots_updated': results['snapshots_updated'],
                'managers_processed': results['managers_processed'],
                'errors': results['errors']
            }
        else:
            logger.error(f"Snapshot generation failed: {results.get('error', 'Unknown error')}")
            raise Exception(f"Snapshot generation failed: {results.get('error', 'Unknown error')}")
            
    except Exception as exc:
        logger.error(f"Error in daily snapshot generation task: {exc}")
        
        # Retry the task with exponential backoff
        if self.request.retries < self.max_retries:
            retry_delay = 60 * (2 ** self.request.retries)  # Exponential backoff
            logger.info(f"Retrying task in {retry_delay} seconds (attempt {self.request.retries + 1})")
            raise self.retry(countdown=retry_delay, exc=exc)
        else:
            # Send failure notification to administrators
            send_task_failure_notification.delay(
                task_name='generate_daily_portfolio_snapshots',
                error_message=str(exc),
                target_date=target_date_str or timezone.now().date().isoformat()
            )
            raise exc


@shared_task
def generate_portfolio_snapshot_for_manager(manager_id, target_date_str=None):
    """
    Generate portfolio snapshot for a specific manager
    
    Args:
        manager_id: UUID of the portfolio manager
        target_date_str: Date string in YYYY-MM-DD format (optional, defaults to today)
        
    Returns:
        Dictionary with task results
    """
    try:
        # Parse target date
        if target_date_str:
            target_date = timezone.datetime.strptime(target_date_str, '%Y-%m-%d').date()
        else:
            target_date = timezone.now().date()
        
        # Get manager
        try:
            manager = CustomUser.objects.get(
                id=manager_id,
                role__in=['loan_officer', 'team_leader'],
                is_active=True
            )
        except CustomUser.DoesNotExist:
            raise Exception(f"Manager with ID {manager_id} not found or not a portfolio manager")
        
        logger.info(f"Generating portfolio snapshot for {manager.get_full_name()} on {target_date}")
        
        # Initialize service and generate snapshot
        snapshot_service = PortfolioSnapshotService()
        snapshot_data = snapshot_service._calculate_portfolio_metrics(manager, target_date)
        
        # Create or update snapshot
        snapshot, created = PortfolioSnapshot.objects.update_or_create(
            manager=manager,
            snapshot_date=target_date,
            defaults=snapshot_data
        )
        
        # Calculate health score and check for alerts
        health_score = snapshot.get_portfolio_health_score()
        snapshot_service._check_performance_alerts(manager, snapshot, health_score)
        
        action = "created" if created else "updated"
        logger.info(f"Successfully {action} snapshot for {manager.get_full_name()} - Health Score: {health_score}%")
        
        return {
            'status': 'success',
            'manager_id': str(manager_id),
            'manager_name': manager.get_full_name(),
            'date': target_date.isoformat(),
            'action': action,
            'health_score': float(health_score)
        }
        
    except Exception as exc:
        logger.error(f"Error generating snapshot for manager {manager_id}: {exc}")
        raise exc


@shared_task
def calculate_portfolio_health_scores(target_date_str=None):
    """
    Calculate and update portfolio health scores for all snapshots on a given date
    
    Args:
        target_date_str: Date string in YYYY-MM-DD format (optional, defaults to today)
        
    Returns:
        Dictionary with calculation results
    """
    try:
        # Parse target date
        if target_date_str:
            target_date = timezone.datetime.strptime(target_date_str, '%Y-%m-%d').date()
        else:
            target_date = timezone.now().date()
        
        logger.info(f"Calculating portfolio health scores for {target_date}")
        
        # Get all snapshots for the target date
        snapshots = PortfolioSnapshot.objects.filter(snapshot_date=target_date)
        
        results = {
            'date': target_date.isoformat(),
            'snapshots_processed': 0,
            'critical_alerts': 0,
            'warning_alerts': 0,
            'healthy_portfolios': 0
        }
        
        snapshot_service = PortfolioSnapshotService()
        
        for snapshot in snapshots:
            try:
                health_score = snapshot.get_portfolio_health_score()
                
                # Check for alerts
                snapshot_service._check_performance_alerts(snapshot.manager, snapshot, health_score)
                
                # Categorize health scores
                if health_score < 50:
                    results['critical_alerts'] += 1
                elif health_score < 70:
                    results['warning_alerts'] += 1
                else:
                    results['healthy_portfolios'] += 1
                
                results['snapshots_processed'] += 1
                
            except Exception as e:
                logger.error(f"Error calculating health score for snapshot {snapshot.id}: {e}")
        
        logger.info(f"Health score calculation completed: {results['snapshots_processed']} processed, "
                   f"{results['critical_alerts']} critical, {results['warning_alerts']} warnings")
        
        return results
        
    except Exception as exc:
        logger.error(f"Error in health score calculation task: {exc}")
        raise exc


@shared_task
def send_weekly_portfolio_summary():
    """
    Send weekly portfolio performance summary to team leaders and administrators
    """
    try:
        logger.info("Generating weekly portfolio summary")
        
        # Get date range for the past week
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=7)
        
        # Get all team leaders and administrators
        recipients = CustomUser.objects.filter(
            role__in=['team_leader', 'admin'],
            is_active=True,
            email__isnull=False
        ).exclude(email='')
        
        snapshot_service = PortfolioSnapshotService()
        
        # Generate summary for each recipient
        for recipient in recipients:
            try:
                # Get portfolio managers under this recipient (if team leader)
                if recipient.role == 'team_leader':
                    managers = CustomUser.objects.filter(
                        role='loan_officer',
                        branch=recipient.branch,
                        is_active=True
                    )
                else:  # Admin - get all managers
                    managers = CustomUser.objects.filter(
                        role__in=['loan_officer', 'team_leader'],
                        is_active=True
                    )
                
                # Generate summary data
                summary_data = []
                for manager in managers:
                    trends = snapshot_service.get_portfolio_trends(manager, 7)
                    if 'error' not in trends:
                        latest_snapshot = PortfolioSnapshot.objects.filter(
                            manager=manager,
                            snapshot_date__range=[start_date, end_date]
                        ).order_by('-snapshot_date').first()
                        
                        if latest_snapshot:
                            summary_data.append({
                                'manager': manager.get_full_name(),
                                'branch': manager.branch.name if manager.branch else 'No Branch',
                                'health_score': latest_snapshot.get_portfolio_health_score(),
                                'total_clients': latest_snapshot.total_clients,
                                'active_loans': latest_snapshot.active_loans,
                                'collection_rate': latest_snapshot.collection_rate,
                                'default_rate': latest_snapshot.default_rate,
                                'client_growth': trends.get('client_growth', 0),
                                'trends': trends
                            })
                
                # Send email summary
                if summary_data:
                    send_portfolio_summary_email.delay(recipient.email, recipient.get_full_name(), summary_data, start_date.isoformat(), end_date.isoformat())
                
            except Exception as e:
                logger.error(f"Error generating summary for {recipient.get_full_name()}: {e}")
        
        logger.info(f"Weekly portfolio summary sent to {recipients.count()} recipients")
        
        return {
            'status': 'success',
            'recipients_count': recipients.count(),
            'period': f"{start_date} to {end_date}"
        }
        
    except Exception as exc:
        logger.error(f"Error in weekly portfolio summary task: {exc}")
        raise exc


@shared_task
def send_snapshot_summary_email(results):
    """
    Send snapshot generation summary email to administrators
    
    Args:
        results: Dictionary with snapshot generation results
    """
    try:
        # Get administrator emails
        admin_emails = list(CustomUser.objects.filter(
            role='admin',
            is_active=True,
            email__isnull=False
        ).exclude(email='').values_list('email', flat=True))
        
        if not admin_emails:
            logger.warning("No administrator emails found for snapshot summary")
            return
        
        subject = f"Daily Portfolio Snapshots Generated - {results['date']}"
        
        message = f"""
Daily Portfolio Snapshot Generation Summary
==========================================

Date: {results['date']}
Managers Processed: {results['managers_processed']}
New Snapshots Created: {results['snapshots_generated']}
Existing Snapshots Updated: {results['snapshots_updated']}
Success: {'Yes' if results['success'] else 'No'}

"""
        
        if results['errors']:
            message += f"Errors Encountered ({len(results['errors'])}):\n"
            for error in results['errors']:
                message += f"  - {error}\n"
        else:
            message += "No errors encountered.\n"
        
        message += """
This is an automated notification from Haven Grazuri Investment Limited.
Please log into the system to review detailed portfolio analytics.

Best regards,
Haven Grazuri Investment Limited Team
"""
        
        send_mail(
            subject=subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=admin_emails,
            fail_silently=False
        )
        
        logger.info(f"Snapshot summary email sent to {len(admin_emails)} administrators")
        
    except Exception as exc:
        logger.error(f"Error sending snapshot summary email: {exc}")
        raise exc


@shared_task
def send_portfolio_summary_email(recipient_email, recipient_name, summary_data, start_date, end_date):
    """
    Send weekly portfolio summary email to a specific recipient
    
    Args:
        recipient_email: Email address of recipient
        recipient_name: Name of recipient
        summary_data: List of portfolio summary data
        start_date: Start date of summary period
        end_date: End date of summary period
    """
    try:
        subject = f"Weekly Portfolio Performance Summary - {start_date} to {end_date}"
        
        message = f"""
Dear {recipient_name},

Here is your weekly portfolio performance summary for the period {start_date} to {end_date}:

Portfolio Manager Performance Summary
====================================

"""
        
        # Add summary for each manager
        for data in summary_data:
            message += f"""
Manager: {data['manager']} ({data['branch']})
  - Health Score: {data['health_score']:.1f}%
  - Total Clients: {data['total_clients']}
  - Active Loans: {data['active_loans']}
  - Collection Rate: {data['collection_rate']:.1f}%
  - Default Rate: {data['default_rate']:.1f}%
  - Client Growth: {data['client_growth']} clients

"""
        
        # Add overall summary
        total_clients = sum(d['total_clients'] for d in summary_data)
        total_loans = sum(d['active_loans'] for d in summary_data)
        avg_health_score = sum(d['health_score'] for d in summary_data) / len(summary_data) if summary_data else 0
        
        message += f"""
Overall Summary
===============
Total Portfolio Managers: {len(summary_data)}
Total Clients: {total_clients}
Total Active Loans: {total_loans}
Average Health Score: {avg_health_score:.1f}%

Please log into the system for detailed analytics and trend analysis.

Best regards,
Haven Grazuri Investment Limited Team
"""
        
        send_mail(
            subject=subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[recipient_email],
            fail_silently=False
        )
        
        logger.info(f"Portfolio summary email sent to {recipient_email}")
        
    except Exception as exc:
        logger.error(f"Error sending portfolio summary email to {recipient_email}: {exc}")
        raise exc


@shared_task
def send_task_failure_notification(task_name, error_message, target_date):
    """
    Send notification when a critical task fails
    
    Args:
        task_name: Name of the failed task
        error_message: Error message
        target_date: Target date for the task
    """
    try:
        # Get administrator emails
        admin_emails = list(CustomUser.objects.filter(
            role='admin',
            is_active=True,
            email__isnull=False
        ).exclude(email='').values_list('email', flat=True))
        
        if not admin_emails:
            logger.warning("No administrator emails found for failure notification")
            return
        
        subject = f"Critical Task Failure: {task_name}"
        
        message = f"""
CRITICAL TASK FAILURE ALERT
===========================

Task: {task_name}
Target Date: {target_date}
Failure Time: {timezone.now().isoformat()}

Error Message:
{error_message}

This task has failed after multiple retry attempts. Please investigate immediately
and manually run the task if necessary.

To manually run the task, use:
python manage.py generate_portfolio_snapshots --date {target_date}

Best regards,
Haven Grazuri Investment Limited Team
"""
        
        send_mail(
            subject=subject,
            message=message,
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=admin_emails,
            fail_silently=False
        )
        
        logger.info(f"Task failure notification sent to {len(admin_emails)} administrators")
        
    except Exception as exc:
        logger.error(f"Error sending task failure notification: {exc}")
        raise exc


@shared_task
def cleanup_old_snapshots(days_to_keep=365):
    """
    Clean up old portfolio snapshots to manage database size
    
    Args:
        days_to_keep: Number of days of snapshots to keep (default: 365)
    """
    try:
        cutoff_date = timezone.now().date() - timedelta(days=days_to_keep)
        
        # Delete old snapshots
        deleted_count, _ = PortfolioSnapshot.objects.filter(
            snapshot_date__lt=cutoff_date
        ).delete()
        
        logger.info(f"Cleaned up {deleted_count} portfolio snapshots older than {cutoff_date}")
        
        return {
            'status': 'success',
            'deleted_count': deleted_count,
            'cutoff_date': cutoff_date.isoformat()
        }
        
    except Exception as exc:
        logger.error(f"Error in snapshot cleanup task: {exc}")
        raise exc