"""
Observations models - Staff observation reporting for Al Hammadi.

This module implements the observation reporting system that:
- Allows anonymous submission (no login required)
- Supports optional staff identification
- Tracks observation lifecycle with status changes
- Links to departments and action center
- Maintains audit trail with notes and status logs
"""

import secrets
import string

from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils import timezone

from apps.core.models import SoftDeleteModel, TimeStampedModel, UUIDModel
from apps.organizations.models import LocationType


def generate_tracking_code():
    """Generate a unique tracking code for observations."""
    # Format: OBS-XXXXXX (6 alphanumeric characters)
    chars = string.ascii_uppercase + string.digits
    code = "".join(secrets.choice(chars) for _ in range(6))
    return f"OBS-{code}"


class ObservationSeverity(models.TextChoices):
    """Observation severity choices."""

    LOW = "low", "Low"
    MEDIUM = "medium", "Medium"
    HIGH = "high", "High"
    CRITICAL = "critical", "Critical"


class ObservationStatus(models.TextChoices):
    """Observation status choices."""

    OPEN = "open", "Open"
    IN_PROGRESS = "in_progress", "In Progress"
    RESOLVED = "resolved", "Resolved"
    CLOSED = "closed", "Closed"


VALID_OBSERVATION_TRANSITIONS = {
    ObservationStatus.OPEN: {ObservationStatus.IN_PROGRESS},
    ObservationStatus.IN_PROGRESS: {ObservationStatus.RESOLVED},
    ObservationStatus.RESOLVED: {ObservationStatus.CLOSED, ObservationStatus.IN_PROGRESS},
    ObservationStatus.CLOSED: {ObservationStatus.IN_PROGRESS},
}


class ObservationCategory(UUIDModel, TimeStampedModel):
    """
    Observation category for classifying reported issues.

    Supports bilingual names (English and Arabic).
    """

    name_en = models.CharField(max_length=200, verbose_name="Name (English)")
    name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
    description = models.TextField(blank=True)

    # Status and ordering
    is_active = models.BooleanField(default=True, db_index=True)
    sort_order = models.IntegerField(default=0, help_text="Lower numbers appear first")

    # Icon for UI (optional)
    icon = models.CharField(max_length=50, blank=True, help_text="Bootstrap icon class")

    class Meta:
        ordering = ["sort_order", "name_en"]
        verbose_name = "Observation Category"
        verbose_name_plural = "Observation Categories"

    def __str__(self):
        return self.name_en

    def get_localized_name(self):
        from django.utils.translation import get_language

        if get_language() == "ar" and self.name_ar:
            return self.name_ar
        return self.name_en

    @property
    def name(self):
        """Return English name as default."""
        return self.name_en


class ObservationSubCategory(UUIDModel, TimeStampedModel):
    """
    Observation sub-category for finer-grained classification.

    Each sub-category belongs to a parent ObservationCategory.
    Supports bilingual names (English and Arabic).
    """

    category = models.ForeignKey(
        ObservationCategory, on_delete=models.CASCADE, related_name="sub_categories"
    )

    name_en = models.CharField(max_length=200, verbose_name="Name (English)")
    name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
    description = models.TextField(blank=True)

    is_active = models.BooleanField(default=True, db_index=True)
    sort_order = models.IntegerField(default=0, help_text="Lower numbers appear first")

    class Meta:
        ordering = ["sort_order", "name_en"]
        verbose_name = "Observation Sub-Category"
        verbose_name_plural = "Observation Sub-Categories"

    def __str__(self):
        return f"{self.category.name_en} / {self.name_en}"

    def get_localized_name(self):
        from django.utils.translation import get_language

        if get_language() == "ar" and self.name_ar:
            return self.name_ar
        return self.name_en

    @property
    def name(self):
        return self.name_en


class ObservationSLAConfig(UUIDModel, TimeStampedModel):
    """
    SLA configuration for observations per hospital and severity.

    Allows flexible SLA configuration for observation resolution times.
    """

    hospital = models.ForeignKey(
        "organizations.Hospital", on_delete=models.CASCADE, related_name="observation_sla_configs"
    )

    severity = models.CharField(
        max_length=20,
        choices=ObservationSeverity.choices,
        null=True,
        blank=True,
        help_text="Severity level for this SLA (optional = default config)",
    )

    sla_hours = models.IntegerField(help_text="Number of hours until SLA deadline")

    first_reminder_hours_after = models.IntegerField(
        default=0, help_text="Send 1st reminder X hours after observation creation (0 = use reminder_hours_before)"
    )

    second_reminder_hours_after = models.IntegerField(
        default=0,
        help_text="Send 2nd reminder X hours after observation creation (0 = use second_reminder_hours_before)",
    )

    escalation_hours_after = models.IntegerField(
        default=0, help_text="Escalate observation X hours after creation if unresolved (0 = use overdue logic)"
    )

    reminder_hours_before = models.IntegerField(default=24, help_text="Send first reminder X hours before deadline")

    second_reminder_enabled = models.BooleanField(default=False, help_text="Enable sending a second reminder")

    second_reminder_hours_before = models.IntegerField(
        default=6, help_text="Send second reminder X hours before deadline"
    )

    is_active = models.BooleanField(default=True)

    # Department response SLA fields
    dept_response_hours = models.IntegerField(
        default=48, help_text="Hours for department to submit a response"
    )
    dept_response_reminder_hours_before = models.IntegerField(
        default=12, help_text="Send 1st reminder X hours before dept response deadline"
    )
    dept_response_second_reminder_enabled = models.BooleanField(
        default=True, help_text="Enable sending a second reminder for dept response"
    )
    dept_response_second_reminder_hours_before = models.IntegerField(
        default=4, help_text="Send 2nd reminder X hours before dept response deadline"
    )
    dept_response_auto_escalate_enabled = models.BooleanField(
        default=True, help_text="Auto-escalate to department manager if response overdue"
    )
    dept_response_escalation_hours_overdue = models.IntegerField(
        default=0, help_text="Escalate X hours after dept response deadline (0 = immediately)"
    )

    class Meta:
        ordering = ["hospital", "severity"]
        verbose_name = "Observation SLA Config"
        verbose_name_plural = "Observation SLA Configs"
        indexes = [
            models.Index(fields=["hospital", "is_active"]),
            models.Index(fields=["hospital", "severity", "is_active"]),
        ]

    def __str__(self):
        sev_display = self.severity if self.severity else "Default"
        return f"{self.hospital.name} - {sev_display} - {self.sla_hours}h"

    def get_first_reminder_hours_after(self, observation_created_at=None):
        if self.first_reminder_hours_after > 0:
            return self.first_reminder_hours_after
        else:
            return max(0, self.sla_hours - self.reminder_hours_before)

    def get_second_reminder_hours_after(self, observation_created_at=None):
        if self.second_reminder_hours_after > 0:
            return self.second_reminder_hours_after
        elif self.second_reminder_enabled:
            return max(0, self.sla_hours - self.second_reminder_hours_before)
        else:
            return 0

    def get_escalation_hours_after(self, observation_created_at=None):
        if self.escalation_hours_after > 0:
            return self.escalation_hours_after
        else:
            return None


class Observation(UUIDModel, TimeStampedModel, SoftDeleteModel):
    """
    Observation - Staff-reported issue or concern.

    Key features:
    - Anonymous submission supported (no login required)
    - Optional reporter identification (staff_id, name)
    - Unique tracking code for public lookup
    - Full lifecycle management with status tracking
    - Links to departments and action center
    """

    # Tracking
    tracking_code = models.CharField(
        max_length=50,
        unique=True,
        default=generate_tracking_code,
        help_text="Unique code for tracking this observation (unified: OBS-YYYYMM-HOSP-NNNN)",
    )

    # Classification
    category = models.ForeignKey(
        ObservationCategory, on_delete=models.SET_NULL, null=True, blank=True, related_name="observations"
    )
    sub_category = models.ForeignKey(
        ObservationSubCategory,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="Observation sub-category for finer classification",
    )

    # AI Taxonomy — 4-level SHCT taxonomy (reuse ComplaintCategory hierarchy)
    taxonomy_domain = models.ForeignKey(
        "complaints.ComplaintCategory",
        on_delete=models.SET_NULL,
        related_name="observations_domain",
        null=True,
        blank=True,
        help_text="Level 1: Domain",
    )
    taxonomy_category = models.ForeignKey(
        "complaints.ComplaintCategory",
        on_delete=models.SET_NULL,
        related_name="observations_category",
        null=True,
        blank=True,
        help_text="Level 2: Category",
    )
    taxonomy_subcategory = models.ForeignKey(
        "complaints.ComplaintCategory",
        on_delete=models.SET_NULL,
        related_name="observations_subcategory",
        null=True,
        blank=True,
        help_text="Level 3: Subcategory",
    )
    taxonomy_classification = models.ForeignKey(
        "complaints.ComplaintCategory",
        on_delete=models.SET_NULL,
        related_name="observations_classification",
        null=True,
        blank=True,
        help_text="Level 4: Classification",
    )

    # Content
    title = models.CharField(max_length=300, blank=True, help_text="Optional short title")
    description = models.TextField(help_text="Detailed description of the observation")
    description_en = models.TextField(blank=True, help_text="English description of the observation")

    # Severity
    severity = models.CharField(
        max_length=20, choices=ObservationSeverity.choices, default=ObservationSeverity.MEDIUM, db_index=True
    )

    # Location and timing
    location_text = models.CharField(
        max_length=500, blank=True, help_text="Where the issue was observed (building, floor, room, etc.)"
    )
    location_type = models.CharField(
        max_length=20, choices=LocationType.choices, blank=True, help_text="Where the observation occurred (OP/IP/ER/GO)"
    )
    area = models.ForeignKey(
        "organizations.Area",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="Physical area within the hospital where the observation was made",
    )
    legacy_location = models.ForeignKey(
        "organizations.LegacyLocation",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="[DEPRECATED] Use 'department' and 'section' fields instead. This field is read-only and will be removed in a future release.",
    )
    legacy_main_section = models.ForeignKey(
        "organizations.LegacyMainSection",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="[DEPRECATED] Use 'department' and 'section' fields instead. This field is read-only and will be removed in a future release.",
    )
    legacy_subsection = models.ForeignKey(
        "organizations.LegacySubSection",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="[DEPRECATED] Use 'department' and 'section' fields instead. This field is read-only and will be removed in a future release.",
    )
    # New hierarchy (from 4th Version Excel)
    section = models.ForeignKey(
        "organizations.Section",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations_new",
        help_text="Section within department",
    )
    incident_datetime = models.DateTimeField(default=timezone.now, help_text="When the issue was observed")

    # Optional reporter information (anonymous supported)
    reporter_staff_id = models.CharField(max_length=50, blank=True, help_text="Optional staff ID of the reporter")
    reporter_name = models.CharField(max_length=200, blank=True, help_text="Optional name of the reporter")
    reporter_phone = models.CharField(max_length=20, blank=True, help_text="Optional phone number for follow-up")
    reporter_email = models.EmailField(blank=True, help_text="Optional email for follow-up")

    # Patient / subject information
    patient_file_number = models.CharField(
        max_length=100, blank=True, help_text="Medical record number / file number of the patient"
    )

    # Status and workflow
    status = models.CharField(
        max_length=25, choices=ObservationStatus.choices, default=ObservationStatus.OPEN, db_index=True
    )

    contact_status = models.CharField(
        max_length=25,
        choices=[
            ("not_contacted", "Not Contacted"),
            ("contacted", "Contacted"),
            ("contacted_no_response", "Contacted - No Response"),
        ],
        default="not_contacted",
        blank=True,
        db_index=True,
    )
    contact_status_at = models.DateTimeField(null=True, blank=True)
    contact_status_by = models.ForeignKey(
        "accounts.User",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="+",
    )

    # Organization (required for tenant isolation)
    hospital = models.ForeignKey(
        "organizations.Hospital",
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name="observations",
        help_text="Hospital where observation was made",
    )

    # Staff member mentioned in observation (optional, for AI-matching like complaints)
    staff = models.ForeignKey(
        "organizations.Staff",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observations",
        help_text="Staff member mentioned in observation",
    )

    # Source tracking - FK to PXSource
    px_source = models.ForeignKey(
        "px_sources.PXSource",
        on_delete=models.PROTECT,
        related_name="observations",
        null=True,
        blank=True,
        help_text="Source of observation",
    )

    # Legacy source field for backward compatibility
    source = models.CharField(
        max_length=50,
        choices=[
            ("staff_portal", "Staff Portal"),
            ("web_form", "Web Form"),
            ("mobile_app", "Mobile App"),
            ("email", "Email"),
            ("call_center", "Call Center"),
            ("other", "Other"),
        ],
        default="staff_portal",
        help_text="How the observation was submitted (legacy)",
    )

    # Internal routing
    assigned_department = models.ForeignKey(
        "organizations.Department",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assigned_observations",
        help_text="Department responsible for handling this observation",
    )
    assigned_to = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="assigned_observations",
        help_text="User assigned to handle this observation",
    )
    assigned_at = models.DateTimeField(null=True, blank=True)

    # Communication tracking (person/department notified + how/when)
    person_noted = models.CharField(
        max_length=300, blank=True, help_text="Person who was informed/notified about the observation"
    )
    department_noted = models.ForeignKey(
        "organizations.Department",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="noted_observations",
        help_text="Department that was notified about this observation",
    )
    communication_method = models.CharField(
        max_length=50, blank=True, help_text="How communication was made (extension, mobile, office, etc.)"
    )
    communication_datetime = models.DateTimeField(
        null=True, blank=True, help_text="When the person/department was contacted"
    )

    # Activation tracking
    activated_at = models.DateTimeField(
        null=True,
        blank=True,
        db_index=True,
        help_text="Timestamp when observation was first activated (moved to IN_PROGRESS)",
    )

    # SLA tracking
    due_at = models.DateTimeField(null=True, blank=True, db_index=True, help_text="SLA deadline")
    is_overdue = models.BooleanField(default=False, db_index=True)
    breached_at = models.DateTimeField(
        null=True, blank=True, db_index=True, help_text="Timestamp when observation first breached SLA"
    )
    reminder_sent_at = models.DateTimeField(null=True, blank=True, help_text="First SLA reminder timestamp")
    second_reminder_sent_at = models.DateTimeField(null=True, blank=True, help_text="Second SLA reminder timestamp")
    escalated_at = models.DateTimeField(null=True, blank=True)

    # Triage information
    triaged_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="triaged_observations"
    )
    triaged_at = models.DateTimeField(null=True, blank=True)

    # Department response (for outgoing observations)
    department_response_en = models.TextField(blank=True, verbose_name="Department Response (English)")
    department_response_ar = models.TextField(blank=True, verbose_name="Department Response (Arabic)")
    department_response_summary_en = models.TextField(blank=True, verbose_name="AI Summary of Dept Response (EN)")
    department_response_summary_ar = models.TextField(blank=True, verbose_name="AI Summary of Dept Response (AR)")
    department_responded_at = models.DateTimeField(null=True, blank=True)
    department_responded_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="department_observation_responses",
    )

    # Forwarded to department tracking
    forwarded_to_dept_at = models.DateTimeField(
        null=True, blank=True, help_text="When the observation was sent to the department",
    )
    sent_to_department = models.BooleanField(
        default=False, help_text="Whether this observation has been sent to the assigned department for visibility"
    )
    sent_to_department_at = models.DateTimeField(
        null=True, blank=True, help_text="When the observation was sent to the assigned department"
    )

    # Department response SLA tracking
    dept_response_sla_due_at = models.DateTimeField(
        null=True, blank=True, db_index=True,
        help_text="SLA deadline for department response",
    )
    dept_response_is_overdue = models.BooleanField(
        default=False, db_index=True,
        help_text="Department response is overdue",
    )
    dept_response_reminder_sent_at = models.DateTimeField(
        null=True, blank=True, help_text="First SLA reminder for dept response",
    )
    dept_response_second_reminder_sent_at = models.DateTimeField(
        null=True, blank=True, help_text="Second SLA reminder for dept response",
    )
    dept_response_escalated_at = models.DateTimeField(
        null=True, blank=True, help_text="When dept response was escalated to manager",
    )

    # Token-based department response
    response_token = models.CharField(
        max_length=100, blank=True, null=True, unique=True, db_index=True,
        help_text="One-time token for department response link",
    )
    response_token_used = models.BooleanField(default=False)
    response_token_sent_at = models.DateTimeField(null=True, blank=True)

    # Department response acceptance review
    dept_response_acceptance_status = models.CharField(
        max_length=20,
        choices=[
            ("pending", "Pending Review"),
            ("acceptable", "Acceptable"),
            ("not_acceptable", "Not Acceptable"),
        ],
        default="pending",
        help_text="Review status of the department response",
    )
    dept_response_accepted_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="reviewed_observation_dept_responses",
        help_text="User who reviewed the department response",
    )
    dept_response_accepted_at = models.DateTimeField(
        null=True, blank=True, help_text="When the department response was reviewed",
    )
    dept_response_acceptance_notes = models.TextField(
        blank=True, help_text="Notes about the acceptance decision",
    )

    # Resolution
    resolved_at = models.DateTimeField(null=True, blank=True)
    resolved_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="resolved_observations"
    )
    resolution_notes = models.TextField(blank=True)

    # Closure
    closed_at = models.DateTimeField(null=True, blank=True)
    closed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True, related_name="closed_observations"
    )

    # Link to Action Center (if converted to action)
    # Using GenericForeignKey on PXAction side, store action_id here for quick reference
    action_id = models.UUIDField(null=True, blank=True, help_text="ID of linked PX Action if converted")

    # Metadata
    client_ip = models.GenericIPAddressField(null=True, blank=True)
    user_agent = models.TextField(blank=True)
    metadata = models.JSONField(default=dict, blank=True)

    # Notification tracking
    submitter_notified_at = models.DateTimeField(
        null=True, blank=True, help_text="When confirmation message was sent to submitter"
    )

    responsible_person_notified_at = models.DateTimeField(
        null=True, blank=True, help_text="When email was sent to responsible person"
    )

    # Monthly follow-up tracking
    monthly_follow_up_due_at = models.DateTimeField(
        null=True, blank=True, db_index=True, help_text="When monthly follow-up is due"
    )

    monthly_follow_up_completed_at = models.DateTimeField(
        null=True, blank=True, help_text="When monthly follow-up was completed"
    )

    monthly_follow_up_completed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="completed_observation_followups",
        help_text="User who completed the monthly follow-up",
    )

    monthly_follow_up_notes = models.TextField(blank=True, help_text="Notes from monthly follow-up")

    notes = GenericRelation("core.Note")

    class Meta:
        ordering = ["-created_at"]
        indexes = [
            models.Index(fields=["hospital", "status", "-created_at"]),
            models.Index(fields=["severity", "-created_at"]),
            models.Index(fields=["assigned_department", "status"]),
            models.Index(fields=["assigned_to", "status"]),
        ]
        permissions = [
            ("triage_observation", "Can triage observations"),
            ("manage_categories", "Can manage observation categories"),
        ]

    def __str__(self):
        return f"{self.tracking_code} - {self.title or self.description[:50]}"

    def save(self, *args, **kwargs):
        """Generate a unified tracking code on creation (OBS-YYYYMM-HOSP-NNNN)."""
        if self._state.adding and self.hospital_id:
            from apps.core.reference import generate_reference

            self.tracking_code = generate_reference("OBS", self.hospital)
        elif not self.tracking_code:
            self.tracking_code = generate_tracking_code()

        # Safety: ensure uniqueness (sequence-based codes are already unique,
        # but guard the random fallback path).
        while Observation.objects.filter(tracking_code=self.tracking_code).exclude(pk=self.pk).exists():
            self.tracking_code = generate_tracking_code()

        super().save(*args, **kwargs)

    @property
    def is_anonymous(self):
        """Check if the observation was submitted anonymously."""
        return not (self.reporter_staff_id or self.reporter_name)

    @property
    def reporter_display(self):
        """Get display name for reporter."""
        if self.reporter_name:
            return self.reporter_name
        if self.reporter_staff_id:
            return f"Staff ID: {self.reporter_staff_id}"
        return "Anonymous"

    def get_absolute_url(self):
        from django.urls import reverse

        return reverse("observations:observation_detail", kwargs={"pk": self.pk})

    def get_owner(self):
        """
        Returns the owner of this observation.
        Cascade: section(champion, supervisor, deputy_supervisor)
        -> department(champion, deputy_manager, supervisor,
           deputy_supervisor, manager_2nd, manager_3rd).
        Returns: Staff instance or None.
        """
        if self.section:
            for role in ("champion", "supervisor", "deputy_supervisor"):
                owner = getattr(self.section, role, None)
                if owner:
                    return owner
        if self.assigned_department:
            dept = self.assigned_department
            for role in ("champion", "deputy_manager",
                         "supervisor", "deputy_supervisor",
                         "manager_2nd", "manager_3rd"):
                owner = getattr(dept, role, None)
                if owner:
                    return owner
        return None

    def get_severity_color(self):
        """Get Bootstrap color class for severity."""
        colors = {
            "low": "success",
            "medium": "warning",
            "high": "danger",
            "critical": "dark",
        }
        return colors.get(self.severity, "secondary")

    def get_status_color(self):
        """Get Bootstrap color class for status."""
        colors = {
            "new": "primary",
            "triaged": "info",
            "assigned": "info",
            "in_progress": "warning",
            "contacted": "purple",
            "resolved": "success",
            "closed": "secondary",
            "rejected": "danger",
            "duplicate": "secondary",
        }
        return colors.get(self.status, "secondary")

    def get_sla_config(self):
        """Get SLA configuration for this observation based on hospital and severity."""
        try:
            if self.severity:
                config = ObservationSLAConfig.objects.filter(
                    hospital=self.hospital,
                    severity=self.severity,
                    is_active=True,
                ).first()
                if config:
                    return config

            config = ObservationSLAConfig.objects.filter(
                hospital=self.hospital,
                is_active=True,
            ).first()
            if config:
                return config

        except Exception:
            pass

        return None

    def check_overdue(self):
        """Check if observation is overdue and update status"""
        inactive_statuses = ["resolved", "closed", "rejected", "duplicate"]
        if self.status in inactive_statuses:
            return False

        if self.due_at and timezone.now() > self.due_at:
            if not self.is_overdue:
                self.is_overdue = True
                self.breached_at = timezone.now()
                self.save(update_fields=["is_overdue", "breached_at"])
            return True
        return False

    @property
    def is_active_status(self):
        """
        Check if observation is in an active status (can be worked on).
        Active statuses: open, in_progress
        Inactive statuses: resolved, closed
        """
        return self.status in ["open", "in_progress"]

    @property
    def has_ai_analysis(self):
        return bool(self.metadata and self.metadata.get("ai_analysis"))

    @property
    def ai_analysis(self):
        return self.metadata.get("ai_analysis", {}) if self.metadata else {}

    @property
    def short_description_en(self):
        return self.ai_analysis.get("short_description_en", "")

    @property
    def short_description_ar(self):
        return self.ai_analysis.get("short_description_ar", "")

    @property
    def ai_brief_en(self):
        return self.ai_analysis.get("brief_summary_en", "")

    @property
    def ai_brief_ar(self):
        return self.ai_analysis.get("brief_summary_ar", "")

    @property
    def suggested_action_en(self):
        return self.ai_analysis.get("suggested_action_en", "")

    @property
    def suggested_action_ar(self):
        return self.ai_analysis.get("suggested_action_ar", "")

    @property
    def suggested_actions(self):
        return self.ai_analysis.get("suggested_actions", [])

    @property
    def emotion(self):
        return self.ai_analysis.get("emotion", "")

    @property
    def emotion_intensity(self):
        return self.ai_analysis.get("emotion_intensity", 0.0)

    @property
    def emotion_confidence(self):
        return self.ai_analysis.get("emotion_confidence", 0.0)

    @property
    def emotion_intensity_percent(self):
        return round(self.emotion_intensity * 100)

    @property
    def emotion_confidence_percent(self):
        return round(self.emotion_confidence * 100)

    @property
    def reasoning_en(self):
        return self.ai_analysis.get("reasoning_en", "")

    @property
    def reasoning_ar(self):
        return self.ai_analysis.get("reasoning_ar", "")


class ObservationAttachment(UUIDModel, TimeStampedModel):
    """
    Attachment for an observation (photos, documents, etc.).
    """

    observation = models.ForeignKey(Observation, on_delete=models.CASCADE, related_name="attachments")

    file = models.FileField(upload_to="observations/%Y/%m/%d/", help_text="Uploaded file")
    filename = models.CharField(max_length=500, blank=True)
    file_type = models.CharField(max_length=100, blank=True)
    file_size = models.IntegerField(default=0, help_text="File size in bytes")

    description = models.CharField(max_length=500, blank=True)

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"{self.observation.tracking_code} - {self.filename}"

    def save(self, *args, **kwargs):
        """Extract file metadata on save."""
        if self.file:
            if not self.filename:
                self.filename = self.file.name
            if not self.file_size and hasattr(self.file, "size"):
                self.file_size = self.file.size
            if not self.file_type:
                import mimetypes

                self.file_type = mimetypes.guess_type(self.file.name)[0] or ""
        super().save(*args, **kwargs)


class ObservationNote(UUIDModel, TimeStampedModel):
    """
    Internal note on an observation.

    Used by PX360 staff to add comments and updates.
    """

    observation = models.ForeignKey(Observation, on_delete=models.CASCADE, related_name="observation_notes")

    note = models.TextField()

    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, related_name="observation_notes"
    )

    # Flag for internal-only notes
    is_internal = models.BooleanField(default=True, help_text="Internal notes are not visible to public")

    class Meta:
        ordering = ["-created_at"]

    def __str__(self):
        return f"Note on {self.observation.tracking_code} by {self.created_by}"


class ObservationStatusLog(UUIDModel, TimeStampedModel):
    """
    Status change log for observations.

    Tracks all status transitions for audit trail.
    """

    observation = models.ForeignKey(Observation, on_delete=models.CASCADE, related_name="status_logs")

    from_status = models.CharField(max_length=25, choices=ObservationStatus.choices, blank=True)
    to_status = models.CharField(max_length=25, choices=ObservationStatus.choices)

    changed_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name="observation_status_changes",
    )

    comment = models.TextField(blank=True, help_text="Optional comment about the status change")

    class Meta:
        ordering = ["-created_at"]
        verbose_name = "Observation Status Log"
        verbose_name_plural = "Observation Status Logs"

    def __str__(self):
        return f"{self.observation.tracking_code}: {self.from_status} → {self.to_status}"
