"""
Physician Celery tasks

This module contains tasks for:
- Calculating monthly physician ratings from surveys
- Updating physician rankings
- Generating performance reports
"""
import logging
from decimal import Decimal

from celery import shared_task
from django.db import transaction
from django.db.models import Avg, Count, Q
from django.utils import timezone

logger = logging.getLogger(__name__)


@shared_task(bind=True, max_retries=3)
def calculate_monthly_physician_ratings(self, year=None, month=None):
    """
    Calculate physician monthly ratings from survey responses.

    This task aggregates all survey responses that mention physicians
    for a given month and creates/updates PhysicianMonthlyRating records.

    Args:
        year: Year to calculate (default: current year)
        month: Month to calculate (default: current month)

    Returns:
        dict: Result with number of ratings calculated
    """
    from apps.organizations.models import Staff
    from apps.physicians.models import PhysicianMonthlyRating
    from apps.surveys.models import SurveyInstance, SurveyResponse

    try:
        # Default to current month if not specified
        now = timezone.now()
        year = year or now.year
        month = month or now.month

        logger.info(f"Calculating physician ratings for {year}-{month:02d}")

        # Get all active physicians
        physicians = Staff.objects.filter(status='active')

        ratings_created = 0
        ratings_updated = 0

        for physician in physicians:
            # Find all completed surveys mentioning this physician
            # This assumes surveys have a physician field or question
            # Adjust based on your actual survey structure

            # Option 1: If surveys have a direct physician field
            surveys = SurveyInstance.objects.filter(
                status='completed',
                completed_at__year=year,
                completed_at__month=month,
                metadata__physician_id=str(physician.id)
            )

            # Option 2: If physician is mentioned in survey responses
            # You may need to adjust this based on your question structure
            physician_responses = SurveyResponse.objects.filter(
                survey_instance__status='completed',
                survey_instance__completed_at__year=year,
                survey_instance__completed_at__month=month,
                question__text__icontains='physician',  # Adjust based on your questions
                text_value__icontains=physician.get_full_name()
            ).values_list('survey_instance_id', flat=True).distinct()

            # Combine both approaches
            survey_ids = set(surveys.values_list('id', flat=True)) | set(physician_responses)

            if not survey_ids:
                logger.debug(f"No surveys found for physician {physician.get_full_name()}")
                continue

            # Get all surveys for this physician
            physician_surveys = SurveyInstance.objects.filter(id__in=survey_ids)

            # Calculate statistics
            total_surveys = physician_surveys.count()

            # Calculate average rating
            avg_score = physician_surveys.aggregate(
                avg=Avg('total_score')
            )['avg']

            if avg_score is None:
                logger.debug(f"No scores found for physician {physician.get_full_name()}")
                continue

            # Count sentiment
            positive_count = physician_surveys.filter(
                total_score__gte=4.0
            ).count()

            neutral_count = physician_surveys.filter(
                total_score__gte=3.0,
                total_score__lt=4.0
            ).count()

            negative_count = physician_surveys.filter(
                total_score__lt=3.0
            ).count()

            # Get MD consult specific rating if available
            md_consult_surveys = physician_surveys.filter(
                survey_template__survey_type='md_consult'
            )
            md_consult_rating = md_consult_surveys.aggregate(
                avg=Avg('total_score')
            )['avg']

            # Create or update rating
            rating, created = PhysicianMonthlyRating.objects.update_or_create(
                staff=physician,
                year=year,
                month=month,
                defaults={
                    'average_rating': Decimal(str(avg_score)),
                    'total_surveys': total_surveys,
                    'positive_count': positive_count,
                    'neutral_count': neutral_count,
                    'negative_count': negative_count,
                    'md_consult_rating': Decimal(str(md_consult_rating)) if md_consult_rating else None,
                    'metadata': {
                        'calculated_at': timezone.now().isoformat(),
                        'survey_ids': [str(sid) for sid in survey_ids]
                    }
                }
            )

            if created:
                ratings_created += 1
            else:
                ratings_updated += 1

            logger.debug(
                f"{'Created' if created else 'Updated'} rating for {physician.get_full_name()}: "
                f"{avg_score:.2f} ({total_surveys} surveys)"
            )

        # Update rankings
        update_physician_rankings.delay(year, month)

        logger.info(
            f"Completed physician ratings calculation for {year}-{month:02d}: "
            f"{ratings_created} created, {ratings_updated} updated"
        )

        return {
            'status': 'success',
            'year': year,
            'month': month,
            'ratings_created': ratings_created,
            'ratings_updated': ratings_updated
        }

    except Exception as e:
        error_msg = f"Error calculating physician ratings: {str(e)}"
        logger.error(error_msg, exc_info=True)

        # Retry the task
        raise self.retry(exc=e, countdown=60 * (self.request.retries + 1))


@shared_task
def update_physician_rankings(year, month):
    """
    Update hospital and department rankings for physicians.

    This calculates the rank of each physician within their hospital
    and department for the specified month.

    Args:
        year: Year
        month: Month

    Returns:
        dict: Result with number of rankings updated
    """
    from apps.organizations.models import Hospital, Department
    from apps.physicians.models import PhysicianMonthlyRating

    try:
        logger.info(f"Updating physician rankings for {year}-{month:02d}")

        rankings_updated = 0

        # Update hospital rankings
        hospitals = Hospital.objects.filter(status='active')

        for hospital in hospitals:
            # Get all ratings for this hospital
            ratings = PhysicianMonthlyRating.objects.filter(
                staff__hospital=hospital,
                year=year,
                month=month
            ).order_by('-average_rating')

            # Assign ranks
            for rank, rating in enumerate(ratings, start=1):
                rating.hospital_rank = rank
                rating.save(update_fields=['hospital_rank'])
                rankings_updated += 1

        # Update department rankings
        departments = Department.objects.filter(status='active')

        for department in departments:
            # Get all ratings for this department
            ratings = PhysicianMonthlyRating.objects.filter(
                staff__department=department,
                year=year,
                month=month
            ).order_by('-average_rating')

            # Assign ranks
            for rank, rating in enumerate(ratings, start=1):
                rating.department_rank = rank
                rating.save(update_fields=['department_rank'])

        logger.info(f"Updated {rankings_updated} physician rankings for {year}-{month:02d}")

        return {
            'status': 'success',
            'year': year,
            'month': month,
            'rankings_updated': rankings_updated
        }

    except Exception as e:
        error_msg = f"Error updating physician rankings: {str(e)}"
        logger.error(error_msg, exc_info=True)
        return {'status': 'error', 'reason': error_msg}


@shared_task
def generate_physician_performance_report(physician_id, year, month):
    """
    Generate detailed performance report for a physician.

    This creates a comprehensive report including:
    - Monthly rating
    - Comparison to previous months
    - Ranking within hospital/department
    - Trend analysis

    Args:
        physician_id: UUID of Physician
        year: Year
        month: Month

    Returns:
        dict: Performance report data
    """
    from apps.organizations.models import Staff
    from apps.physicians.models import PhysicianMonthlyRating

    try:
        physician = Staff.objects.get(id=physician_id)

        # Get current month rating
        current_rating = PhysicianMonthlyRating.objects.filter(
            staff=physician,
            year=year,
            month=month
        ).first()

        if not current_rating:
            return {
                'status': 'no_data',
                'reason': f'No rating found for {year}-{month:02d}'
            }

        # Get previous month
        prev_month = month - 1 if month > 1 else 12
        prev_year = year if month > 1 else year - 1

        previous_rating = PhysicianMonthlyRating.objects.filter(
            staff=physician,
            year=prev_year,
            month=prev_month
        ).first()

        # Get year-to-date stats
        ytd_ratings = PhysicianMonthlyRating.objects.filter(
            staff=physician,
            year=year
        )

        ytd_avg = ytd_ratings.aggregate(avg=Avg('average_rating'))['avg']
        ytd_surveys = ytd_ratings.aggregate(total=Count('total_surveys'))['total']

        # Calculate trend
        trend = 'stable'
        if previous_rating:
            diff = float(current_rating.average_rating - previous_rating.average_rating)
            if diff > 0.1:
                trend = 'improving'
            elif diff < -0.1:
                trend = 'declining'

        report = {
            'status': 'success',
            'physician': {
                'id': str(physician.id),
                'name': physician.get_full_name(),
                'license': physician.license_number,
                'specialization': physician.specialization
            },
            'current_month': {
                'year': year,
                'month': month,
                'average_rating': float(current_rating.average_rating),
                'total_surveys': current_rating.total_surveys,
                'hospital_rank': current_rating.hospital_rank,
                'department_rank': current_rating.department_rank
            },
            'previous_month': {
                'average_rating': float(previous_rating.average_rating) if previous_rating else None,
                'total_surveys': previous_rating.total_surveys if previous_rating else None
            } if previous_rating else None,
            'year_to_date': {
                'average_rating': float(ytd_avg) if ytd_avg else None,
                'total_surveys': ytd_surveys
            },
            'trend': trend
        }

        logger.info(f"Generated performance report for {physician.get_full_name()}")

        return report

    except Staff.DoesNotExist:
        error_msg = f"Physician {physician_id} not found"
        logger.error(error_msg)
        return {'status': 'error', 'reason': error_msg}

    except Exception as e:
        error_msg = f"Error generating performance report: {str(e)}"
        logger.error(error_msg, exc_info=True)
        return {'status': 'error', 'reason': error_msg}


@shared_task
def schedule_monthly_rating_calculation():
    """
    Scheduled task to calculate physician ratings for the previous month.

    This should be run on the 1st of each month to calculate ratings
    for the previous month.

    Returns:
        dict: Result of calculation
    """
    from dateutil.relativedelta import relativedelta

    # Calculate for previous month
    now = timezone.now()
    prev_month = now - relativedelta(months=1)

    year = prev_month.year
    month = prev_month.month

    logger.info(f"Scheduled calculation of physician ratings for {year}-{month:02d}")

    # Trigger calculation
    result = calculate_monthly_physician_ratings.delay(year, month)

    return {
        'status': 'scheduled',
        'year': year,
        'month': month,
        'task_id': result.id
    }
