"""
Observations Celery tasks - SLA tracking and notifications.

This module implements tasks for:
- Checking overdue observations
- Sending SLA reminder emails
- Escalation handling
"""

import datetime
import logging

from celery import shared_task
from django.utils import timezone

logger = logging.getLogger(__name__)


@shared_task
def check_overdue_observations():
    """
    Periodic task to check for overdue observations.

    Runs every 15 minutes (configured in config/celery.py).
    Updates is_overdue flag and sets breached_at for observations past their SLA deadline.
    """
    from apps.observations.models import Observation, ObservationStatus

    active_statuses = [
        ObservationStatus.OPEN,
        ObservationStatus.IN_PROGRESS,
    ]
    active_observations = Observation.objects.filter(
        status__in=active_statuses,
        due_at__isnull=False,
    ).select_related("hospital", "assigned_department")

    overdue_count = 0

    for observation in active_observations:
        if observation.check_overdue():
            overdue_count += 1
            logger.warning(
                f"Observation {observation.id} is overdue: {observation.tracking_code} (due: {observation.due_at})"
            )

    if overdue_count > 0:
        logger.info(f"Found {overdue_count} overdue observations")

    return {"overdue_count": overdue_count}


@shared_task
def send_observation_sla_reminders():
    """
    Periodic task to send SLA reminder emails for observations approaching deadline.

    Runs every hour (configured in config/celery.py).
    """
    from django.conf import settings
    from django.template.loader import render_to_string

    from apps.observations.models import Observation, ObservationStatus, ObservationSLAConfig
    from apps.notifications.services import NotificationService

    now = timezone.now()
    active_statuses = [
        ObservationStatus.OPEN,
        ObservationStatus.IN_PROGRESS,
    ]

    active_observations = Observation.objects.filter(
        status__in=active_statuses,
        due_at__isnull=False,
        is_overdue=False,
    ).select_related("hospital", "assigned_to", "category")

    first_reminder_count = 0
    second_reminder_count = 0

    for observation in active_observations:
        config = observation.get_sla_config()
        if not config:
            continue

        first_reminder_hours = config.get_first_reminder_hours_after()
        second_reminder_hours = config.get_second_reminder_hours_after()

        if first_reminder_hours > 0 and observation.reminder_sent_at is None:
            hours_since_creation = (now - observation.created_at).total_seconds() / 3600
            if hours_since_creation >= first_reminder_hours:
                if observation.assigned_to and observation.assigned_to.email:
                    try:
                        hours_remaining = (observation.due_at - now).total_seconds() / 3600
                        hours_remaining = max(0, round(hours_remaining))

                        plain_message = (
                            f"Observation '{observation.title or observation.description[:50]}' "
                            f"(tracking code: {observation.tracking_code}) is due at "
                            f"{observation.due_at.strftime('%Y-%m-%d %H:%M')}. "
                            f"Time remaining: {hours_remaining} hours. Please take action."
                        )

                        try:
                            from django.contrib.sites.models import Site

                            site_url = Site.objects.get_current().domain
                        except Exception:
                            site_url = getattr(settings, "SITE_URL", "")

                        context = {
                            "recipient_name": observation.assigned_to.get_full_name(),
                            "observation": observation,
                            "observation_url": f"{site_url}/observations/{observation.id}/",
                            "site_url": site_url,
                            "due_date": observation.due_at,
                            "hours_remaining": hours_remaining,
                            "is_unassigned": False,
                        }
                        html_message = render_to_string("emails/observation_sla_reminder.html", context)

                        NotificationService.send_email(
                            email=observation.assigned_to.email,
                            subject=f"SLA Reminder - Observation {observation.tracking_code}",
                            message=plain_message,
                            html_message=html_message,
                            related_object=observation,
                        )
                        observation.reminder_sent_at = now
                        observation.save(update_fields=["reminder_sent_at"])
                        first_reminder_count += 1
                    except Exception as e:
                        logger.error(f"Failed to send observation reminder: {e}")

        if second_reminder_hours > 0 and observation.second_reminder_sent_at is None:
            hours_since_creation = (now - observation.created_at).total_seconds() / 3600
            if hours_since_creation >= second_reminder_hours:
                if observation.assigned_to and observation.assigned_to.email:
                    try:
                        hours_remaining = (observation.due_at - now).total_seconds() / 3600
                        hours_remaining = max(0, round(hours_remaining))

                        plain_message = (
                            f"URGENT: Observation '{observation.title or observation.description[:50]}' "
                            f"(tracking code: {observation.tracking_code}) is due at "
                            f"{observation.due_at.strftime('%Y-%m-%d %H:%M')}. "
                            f"Time remaining: {hours_remaining} hours. Immediate action required."
                        )

                        try:
                            from django.contrib.sites.models import Site

                            site_url = Site.objects.get_current().domain
                        except Exception:
                            site_url = getattr(settings, "SITE_URL", "")

                        context = {
                            "recipient_name": observation.assigned_to.get_full_name(),
                            "observation": observation,
                            "observation_url": f"{site_url}/observations/{observation.id}/",
                            "site_url": site_url,
                            "due_date": observation.due_at,
                            "hours_remaining": hours_remaining,
                        }
                        html_message = render_to_string("emails/observation_sla_second_reminder.html", context)

                        NotificationService.send_email(
                            email=observation.assigned_to.email,
                            subject=f"URGENT: SLA Reminder - Observation {observation.tracking_code}",
                            message=plain_message,
                            html_message=html_message,
                            related_object=observation,
                        )
                        observation.second_reminder_sent_at = now
                        observation.save(update_fields=["second_reminder_sent_at"])
                        second_reminder_count += 1
                    except Exception as e:
                        logger.error(f"Failed to send second observation reminder: {e}")

    logger.info(
        f"Sent {first_reminder_count} first reminders and {second_reminder_count} second reminders for observations"
    )

    return {
        "first_reminder_count": first_reminder_count,
        "second_reminder_count": second_reminder_count,
    }


@shared_task
def schedule_monthly_followups():
    from apps.observations.models import Observation, ObservationStatus

    now = timezone.now()
    resolved_observations = Observation.objects.filter(
        status__in=[ObservationStatus.RESOLVED, ObservationStatus.CLOSED],
        monthly_follow_up_due_at__isnull=True,
        resolved_at__isnull=False,
    )

    scheduled = 0
    for obs in resolved_observations:
        obs.monthly_follow_up_due_at = obs.resolved_at + datetime.timedelta(days=30)
        obs.save(update_fields=["monthly_follow_up_due_at"])
        scheduled += 1

    logger.info(f"Scheduled {scheduled} monthly observation follow-ups")
    return {"scheduled": scheduled}


@shared_task
def process_monthly_followups():
    from django.conf import settings
    from django.template.loader import render_to_string

    from apps.observations.models import Observation, ObservationStatus
    from apps.notifications.services import NotificationService

    now = timezone.now()
    due_observations = Observation.objects.filter(
        status__in=[ObservationStatus.RESOLVED, ObservationStatus.CLOSED],
        monthly_follow_up_due_at__lte=now,
        monthly_follow_up_completed_at__isnull=True,
    ).select_related("hospital", "assigned_to", "assigned_department", "category")

    notified = 0
    for obs in due_observations:
        if obs.assigned_to and obs.assigned_to.email:
            try:
                plain_message = (
                    f"Observation '{obs.title or obs.description[:50]}' "
                    f"(tracking code: {obs.tracking_code}) requires monthly follow-up review. "
                    f"Please verify that the issue has been fully addressed and sustained."
                )

                try:
                    from django.contrib.sites.models import Site

                    site_url = Site.objects.get_current().domain
                except Exception:
                    site_url = getattr(settings, "SITE_URL", "")

                context = {
                    "recipient_name": obs.assigned_to.get_full_name(),
                    "observation": obs,
                    "observation_url": f"{site_url}/observations/{obs.id}/",
                    "site_url": site_url,
                }
                html_message = render_to_string("emails/observation_monthly_followup.html", context)

                NotificationService.send_email(
                    email=obs.assigned_to.email,
                    subject=f"Monthly Follow-Up Due: Observation {obs.tracking_code}",
                    message=plain_message,
                    html_message=html_message,
                    related_object=obs,
                )
                notified += 1
            except Exception as e:
                logger.error(f"Failed to send follow-up notification for observation {obs.id}: {e}")

    logger.info(f"Sent {notified} monthly follow-up notifications for observations")
    return {"notified": notified}


@shared_task
def send_new_observation_notification(observation_id):
    """
    Send notification for a new observation to PX360 triage team.

    Runs as a background task to avoid blocking the observation creation response.
    """
    from apps.observations.models import Observation
    from apps.observations.services import ObservationService

    try:
        observation = Observation.objects.get(id=observation_id)
        ObservationService.notify_new_observation(observation)
    except Observation.DoesNotExist:
        logger.error(f"Observation {observation_id} not found for notification")


@shared_task
def analyze_observation_with_ai(observation_id):
    """
    Analyze an observation using AI to determine severity, category, and generate
    bilingual summaries and suggested PX actions.

    This task is triggered when an observation is created.
    It uses the AI service to analyze the observation content and classify it.

    Args:
        observation_id: UUID of the Observation

    Returns:
        dict: Result with severity, category, suggested actions, and summaries
    """
    from apps.observations.models import Observation, ObservationCategory, ObservationNote
    from apps.core.ai_service import AIService, AIServiceError

    try:
        observation = Observation.objects.select_related("hospital").get(id=observation_id)

        logger.info(f"Starting AI analysis for observation {observation_id}")

        try:
            analysis = AIService.analyze_observation(
                title=observation.title or None,
                description=observation.description,
                hospital_id=observation.hospital.id if observation.hospital else None,
            )

            emotion_analysis = AIService.analyze_emotion(text=observation.description)

            old_severity = observation.severity
            old_category = observation.category

            # Update severity from AI
            observation.severity = analysis["severity"]

            # Match AI category to ObservationCategory
            ai_category_name = analysis.get("category", "")
            if ai_category_name:
                category = ObservationCategory.objects.filter(name_en__iexact=ai_category_name, is_active=True).first()
                if not category:
                    category = ObservationCategory.objects.filter(
                        name_en__icontains=ai_category_name, is_active=True
                    ).first()
                if category:
                    observation.category = category
                    logger.info(f"AI matched observation category: {category.name_en}")
                else:
                    logger.warning(f"AI suggested category '{ai_category_name}' but no match found")

            # Update title from AI
            if analysis.get("title_en"):
                observation.title = analysis["title_en"]
            elif analysis.get("title"):
                observation.title = analysis["title"]

            # Store full analysis in metadata
            if not observation.metadata:
                observation.metadata = {}

            old_category_name = old_category.name_en if old_category else None

            observation.metadata["ai_analysis"] = {
                "title_en": analysis.get("title_en", ""),
                "title_ar": analysis.get("title_ar", ""),
                "brief_summary_en": analysis.get("brief_summary_en", ""),
                "brief_summary_ar": analysis.get("brief_summary_ar", ""),
                "short_description_en": analysis.get("short_description_en", ""),
                "short_description_ar": analysis.get("short_description_ar", ""),
                "suggested_actions": analysis.get("suggested_actions", []),
                "suggested_action_en": analysis.get("suggested_action_en", ""),
                "suggested_action_ar": analysis.get("suggested_action_ar", ""),
                "severity": analysis.get("severity", ""),
                "category": analysis.get("category", ""),
                "department": analysis.get("department", ""),
                "reasoning_en": analysis.get("reasoning_en", ""),
                "reasoning_ar": analysis.get("reasoning_ar", ""),
                "emotion": emotion_analysis.get("emotion", "neutral"),
                "emotion_intensity": emotion_analysis.get("intensity", 0.0),
                "emotion_confidence": emotion_analysis.get("confidence", 0.0),
                "analyzed_at": timezone.now().isoformat(),
                "old_severity": old_severity,
                "old_category": old_category_name,
            }

            taxonomy_mapping = analysis.get("taxonomy_mapping", {})
            if taxonomy_mapping:
                if taxonomy_mapping.get("domain"):
                    observation.taxonomy_domain_id = taxonomy_mapping["domain"].get("id")
                if taxonomy_mapping.get("category"):
                    observation.taxonomy_category_id = taxonomy_mapping["category"].get("id")
                if taxonomy_mapping.get("subcategory"):
                    observation.taxonomy_subcategory_id = taxonomy_mapping["subcategory"].get("id")
                if taxonomy_mapping.get("classification"):
                    observation.taxonomy_classification_id = taxonomy_mapping["classification"].get("id")
                observation.metadata["taxonomy"] = analysis.get("taxonomy")

            observation.save(
                update_fields=[
                    "severity",
                    "category",
                    "title",
                    "metadata",
                    "taxonomy_domain",
                    "taxonomy_category",
                    "taxonomy_subcategory",
                    "taxonomy_classification",
                ]
            )

            # Create timeline note with bilingual AI completion message
            emotion_display = emotion_analysis.get("emotion", "neutral")
            category_display = observation.category.name_en if observation.category else analysis.get("category", "N/A")

            message_en = (
                f"AI analysis complete: Severity={analysis['severity']}, "
                f"Category={category_display}, "
                f"Emotion={emotion_display}"
            )
            message_ar = (
                f"اكتمل تحليل الذكاء الاصطناعي: الشدة={analysis['severity']}, "
                f"الفئة={category_display}, "
                f"العاطفة={emotion_display}"
            )

            ObservationNote.objects.create(
                observation=observation,
                note=f"{message_en}\n{message_ar}",
                is_internal=True,
                created_by=None,
            )

            logger.info(f"AI analysis completed for observation {observation_id}")
            return {
                "status": "success",
                "observation_id": str(observation_id),
                "severity": analysis.get("severity"),
                "category": analysis.get("category"),
                "suggested_actions_count": len(analysis.get("suggested_actions", [])),
            }

        except AIServiceError as e:
            logger.error(f"AI analysis failed for observation {observation_id}: {e}")
            if not observation.metadata:
                observation.metadata = {}
            observation.metadata["ai_analysis"] = {
                "error": str(e),
                "analyzed_at": timezone.now().isoformat(),
                "reasoning_en": f"AI service unavailable: {str(e)}",
                "reasoning_ar": f"خدمة الذكاء الاصطناعي غير متوفرة: {str(e)}",
            }
            observation.save(update_fields=["metadata"])
            return {"status": "error", "reason": f"AI service error: {str(e)}"}

    except Observation.DoesNotExist:
        logger.error(f"Observation {observation_id} not found for AI analysis")
        return {"status": "error", "reason": "Observation not found"}
    except Exception as e:
        logger.error(f"Unexpected error analyzing observation {observation_id}: {e}", exc_info=True)
        return {"status": "error", "reason": str(e)}


@shared_task
def check_overdue_observation_dept_responses():
    """
    Periodic task to check for overdue department responses on observations.

    Runs every 15 minutes. Flags observations where department response SLA has passed.
    Auto-escalates to department manager if configured.
    """
    from apps.observations.models import Observation, ObservationSLAConfig, ObservationStatusLog, ObservationNote
    from apps.notifications.services import NotificationService

    now = timezone.now()

    pending_observations = Observation.objects.filter(
        dept_response_sla_due_at__isnull=False,
        dept_response_sla_due_at__lte=now,
        department_responded_at__isnull=True,
    ).select_related("hospital", "assigned_department")

    overdue_count = 0
    escalated_count = 0

    for observation in pending_observations:
        if not observation.dept_response_is_overdue:
            observation.dept_response_is_overdue = True
            observation.save(update_fields=["dept_response_is_overdue"])
            overdue_count += 1
            logger.warning(f"Observation {observation.tracking_code} dept response is overdue")

        dept = observation.assigned_department
        if not dept:
            continue

        sla_config = observation.get_sla_config()
        if not sla_config or not sla_config.dept_response_auto_escalate_enabled:
            continue

        escalation_delay = sla_config.dept_response_escalation_hours_overdue
        if escalation_delay > 0:
            escalation_threshold = observation.dept_response_sla_due_at + datetime.timedelta(hours=escalation_delay)
            if now < escalation_threshold:
                continue

        if observation.dept_response_escalated_at:
            continue

        if not (
            observation.dept_response_reminder_sent_at
            or observation.dept_response_second_reminder_sent_at
        ):
            continue

        # Auto-escalation disabled — manual escalation only
        logger.info(f"Auto-escalation skipped for observation {observation.tracking_code} (disabled)")
        continue

    if overdue_count > 0 or escalated_count > 0:
        logger.info(
            f"Observation dept response: {overdue_count} overdue, {escalated_count} escalated"
        )

    return {"overdue_count": overdue_count, "escalated_count": escalated_count}


@shared_task
def send_observation_dept_response_reminders():
    """
    Periodic task to send SLA reminder emails for department responses on observations.

    Runs every hour. Sends first and second reminders based on config.
    """
    from apps.observations.models import Observation, ObservationSLAConfig
    from apps.notifications.services import NotificationService

    now = timezone.now()

    pending_observations = Observation.objects.filter(
        dept_response_sla_due_at__isnull=False,
        department_responded_at__isnull=True,
        dept_response_is_overdue=False,
    ).select_related("hospital", "assigned_department")

    first_reminder_count = 0
    second_reminder_count = 0

    for observation in pending_observations:
        sla_config = observation.get_sla_config()
        if not sla_config:
            continue

        dept = observation.assigned_department
        if not dept:
            continue

        recipients = []
        if dept.champion and dept.champion.user and dept.champion.user.email:
            recipients.append(dept.champion.user)

        if not recipients:
            continue

        hours_remaining = (observation.dept_response_sla_due_at - now).total_seconds() / 3600

        if (
            observation.dept_response_reminder_sent_at is None
            and hours_remaining <= sla_config.dept_response_reminder_hours_before
            and hours_remaining > 0
        ):
            for recipient in recipients:
                try:
                    NotificationService.send_email(
                        email=recipient.email,
                        subject=f"Reminder: Observation {observation.tracking_code} - Response Required",
                        message=(
                            f"This is a reminder that observation {observation.tracking_code} "
                            f"is awaiting your department's response. "
                            f"Time remaining: {max(0, round(hours_remaining))} hours. "
                            f"Please submit your response before the deadline."
                        ),
                        related_object=observation,
                    )
                except Exception as e:
                    logger.error(f"Failed to send observation dept reminder: {e}")

            observation.dept_response_reminder_sent_at = now
            observation.save(update_fields=["dept_response_reminder_sent_at"])
            first_reminder_count += 1

        elif (
            sla_config.dept_response_second_reminder_enabled
            and observation.dept_response_reminder_sent_at is not None
            and observation.dept_response_second_reminder_sent_at is None
            and hours_remaining <= sla_config.dept_response_second_reminder_hours_before
            and hours_remaining > 0
        ):
            for recipient in recipients:
                try:
                    NotificationService.send_email(
                        email=recipient.email,
                        subject=f"URGENT: Observation {observation.tracking_code} - Response Overdue Soon",
                        message=(
                            f"URGENT: Observation {observation.tracking_code} "
                            f"response deadline is approaching. "
                            f"Time remaining: {max(0, round(hours_remaining))} hours. "
                            f"Please submit your response immediately."
                        ),
                        related_object=observation,
                    )
                except Exception as e:
                    logger.error(f"Failed to send observation dept 2nd reminder: {e}")

            observation.dept_response_second_reminder_sent_at = now
            observation.save(update_fields=["dept_response_second_reminder_sent_at"])
            second_reminder_count += 1

    logger.info(
        f"Observation dept response reminders: {first_reminder_count} first, {second_reminder_count} second"
    )

    return {
        "first_reminder_count": first_reminder_count,
        "second_reminder_count": second_reminder_count,
    }
