"""
Surveys models - Survey templates and instances

This module implements the survey system that:
- Defines bilingual survey templates with questions
- Creates survey instances linked to journey stages
- Generates secure token-based survey links
- Collects and scores survey responses
- Triggers actions based on negative feedback
"""
import secrets

from django.core.signing import Signer
from django.db import models
from django.urls import reverse

from apps.core.models import BaseChoices, StatusChoices, TenantModel, TimeStampedModel, UUIDModel


class QuestionType(BaseChoices):
    """Survey question type choices"""
    RATING = 'rating', 'Rating (1-5 stars)'
    NPS = 'nps', 'NPS (0-10)'
    YES_NO = 'yes_no', 'Yes/No'
    MULTIPLE_CHOICE = 'multiple_choice', 'Multiple Choice'
    TEXT = 'text', 'Text (Short Answer)'
    TEXTAREA = 'textarea', 'Text Area (Long Answer)'
    LIKERT = 'likert', 'Likert Scale (1-5)'


class SurveyTemplate(UUIDModel, TimeStampedModel):
    """
    Survey template defines questions for a survey.

    Supports:
    - Bilingual questions (AR/EN)
    - Multiple question types
    - Scoring configuration
    - Branch logic (conditional questions)
    """
    name = models.CharField(max_length=200)
    name_ar = models.CharField(max_length=200, blank=True, verbose_name="Name (Arabic)")
    description = models.TextField(blank=True)
    description_ar = models.TextField(blank=True, verbose_name="Description (Arabic)")

    # Configuration
    hospital = models.ForeignKey(
        'organizations.Hospital',
        on_delete=models.CASCADE,
        related_name='survey_templates'
    )

    # Survey type
    survey_type = models.CharField(
        max_length=50,
        choices=[
            ('stage', 'Journey Stage Survey'),
            ('complaint_resolution', 'Complaint Resolution Satisfaction'),
            ('general', 'General Feedback'),
            ('nps', 'Net Promoter Score'),
        ],
        default='stage',
        db_index=True
    )

    # Scoring configuration
    scoring_method = models.CharField(
        max_length=20,
        choices=[
            ('average', 'Average Score'),
            ('weighted', 'Weighted Average'),
            ('nps', 'NPS Calculation'),
        ],
        default='average'
    )
    negative_threshold = models.DecimalField(
        max_digits=3,
        decimal_places=1,
        default=3.0,
        help_text="Scores below this trigger PX actions (out of 5)"
    )

    # Configuration
    is_active = models.BooleanField(default=True, db_index=True)

    # Metadata
    version = models.IntegerField(default=1)

    class Meta:
        ordering = ['hospital', 'name']
        indexes = [
            models.Index(fields=['hospital', 'survey_type', 'is_active']),
        ]

    def __str__(self):
        return self.name

    def get_question_count(self):
        """Get number of questions"""
        return self.questions.count()


class SurveyQuestion(UUIDModel, TimeStampedModel):
    """
    Survey question within a template.

    Supports:
    - Bilingual text (AR/EN)
    - Multiple question types
    - Required/optional
    - Conditional display (branch logic)
    """
    survey_template = models.ForeignKey(
        SurveyTemplate,
        on_delete=models.CASCADE,
        related_name='questions'
    )

    # Question text
    text = models.TextField(verbose_name="Question Text (English)")
    text_ar = models.TextField(blank=True, verbose_name="Question Text (Arabic)")

    # Question configuration
    question_type = models.CharField(
        max_length=20,
        choices=QuestionType.choices,
        default=QuestionType.RATING
    )
    order = models.IntegerField(default=0, help_text="Display order")
    is_required = models.BooleanField(default=True)

    # For multiple choice questions
    choices_json = models.JSONField(
        default=list,
        blank=True,
        help_text="Array of choice objects: [{'value': '1', 'label': 'Option 1', 'label_ar': 'خيار 1'}]"
    )

    # Scoring
    weight = models.DecimalField(
        max_digits=3,
        decimal_places=2,
        default=1.0,
        help_text="Weight for weighted average scoring"
    )

    # Branch logic
    branch_logic = models.JSONField(
        default=dict,
        blank=True,
        help_text="Conditional display logic: {'show_if': {'question_id': 'value'}}"
    )

    # Help text
    help_text = models.TextField(blank=True)
    help_text_ar = models.TextField(blank=True)

    class Meta:
        ordering = ['survey_template', 'order']
        indexes = [
            models.Index(fields=['survey_template', 'order']),
        ]

    def __str__(self):
        return f"{self.survey_template.name} - Q{self.order}: {self.text[:50]}"


class SurveyInstance(UUIDModel, TimeStampedModel, TenantModel):
    """
    Survey instance - an actual survey sent to a patient.

    Linked to:
    - Survey template (defines questions)
    - Patient (recipient)
    - Journey stage (optional - if stage survey)
    - Encounter (optional)

    Tenant-aware: All surveys are scoped to a hospital.
    """
    survey_template = models.ForeignKey(
        SurveyTemplate,
        on_delete=models.PROTECT,
        related_name='instances'
    )

    # Patient information
    patient = models.ForeignKey(
        'organizations.Patient',
        on_delete=models.CASCADE,
        related_name='surveys'
    )

    # Journey linkage (for stage surveys)
    journey_instance = models.ForeignKey(
        'journeys.PatientJourneyInstance',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='surveys'
    )
    journey_stage_instance = models.ForeignKey(
        'journeys.PatientJourneyStageInstance',
        on_delete=models.CASCADE,
        null=True,
        blank=True,
        related_name='surveys'
    )
    encounter_id = models.CharField(max_length=100, blank=True, db_index=True)

    # Delivery
    delivery_channel = models.CharField(
        max_length=20,
        choices=[
            ('sms', 'SMS'),
            ('whatsapp', 'WhatsApp'),
            ('email', 'Email'),
        ],
        default='sms'
    )
    recipient_phone = models.CharField(max_length=20, blank=True)
    recipient_email = models.EmailField(blank=True)

    # Access token for secure link
    access_token = models.CharField(
        max_length=100,
        unique=True,
        db_index=True,
        blank=True,
        help_text="Secure token for survey access"
    )
    token_expires_at = models.DateTimeField(
        null=True,
        blank=True,
        help_text="Token expiration date"
    )

    # Status
    status = models.CharField(
        max_length=20,
        choices=StatusChoices.choices,
        default=StatusChoices.PENDING,
        db_index=True
    )

    # Timestamps
    sent_at = models.DateTimeField(null=True, blank=True, db_index=True)
    opened_at = models.DateTimeField(null=True, blank=True)
    completed_at = models.DateTimeField(null=True, blank=True)

    # Scoring
    total_score = models.DecimalField(
        max_digits=5,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="Calculated total score"
    )
    is_negative = models.BooleanField(
        default=False,
        db_index=True,
        help_text="True if score below threshold"
    )

    # Metadata
    metadata = models.JSONField(default=dict, blank=True)

    # Patient contact tracking (for negative surveys)
    patient_contacted = models.BooleanField(
        default=False,
        help_text="Whether patient was contacted about negative survey"
    )
    patient_contacted_at = models.DateTimeField(null=True, blank=True)
    patient_contacted_by = models.ForeignKey(
        'accounts.User',
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        related_name='contacted_surveys',
        help_text="User who contacted the patient"
    )
    contact_notes = models.TextField(
        blank=True,
        help_text="Notes from patient contact"
    )
    issue_resolved = models.BooleanField(
        default=False,
        help_text="Whether the issue was resolved/explained"
    )

    # Satisfaction feedback tracking
    satisfaction_feedback_sent = models.BooleanField(
        default=False,
        help_text="Whether satisfaction feedback form was sent"
    )
    satisfaction_feedback_sent_at = models.DateTimeField(null=True, blank=True)

    class Meta:
        ordering = ['-created_at']
        indexes = [
            models.Index(fields=['patient', '-created_at']),
            models.Index(fields=['status', '-sent_at']),
            models.Index(fields=['is_negative', '-completed_at']),
        ]

    def __str__(self):
        return f"{self.survey_template.name} - {self.patient.get_full_name()}"

    def save(self, *args, **kwargs):
        """Generate access token on creation"""
        if not self.access_token:
            self.access_token = secrets.token_urlsafe(32)

        # Set token expiration
        if not self.token_expires_at:
            from datetime import timedelta
            from django.conf import settings
            from django.utils import timezone
            days = getattr(settings, 'SURVEY_TOKEN_EXPIRY_DAYS', 30)
            self.token_expires_at = timezone.now() + timedelta(days=days)

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

    def get_survey_url(self):
        """Generate secure survey URL"""
        # TODO: Implement in Phase 4 UI
        return f"/surveys/{self.access_token}/"

    def calculate_score(self):
        """
        Calculate total score from responses.

        Returns the calculated score and updates the instance.
        """
        responses = self.responses.all()
        if not responses.exists():
            return None

        if self.survey_template.scoring_method == 'average':
            # Simple average of all rating responses
            rating_responses = responses.filter(
                question__question_type__in=['rating', 'likert', 'nps']
            )
            if rating_responses.exists():
                total = sum(float(r.numeric_value or 0) for r in rating_responses)
                count = rating_responses.count()
                score = total / count if count > 0 else 0
            else:
                score = 0

        elif self.survey_template.scoring_method == 'weighted':
            # Weighted average based on question weights
            total_weighted = 0
            total_weight = 0
            for response in responses:
                if response.numeric_value and response.question.weight:
                    total_weighted += float(response.numeric_value) * float(response.question.weight)
                    total_weight += float(response.question.weight)
            score = total_weighted / total_weight if total_weight > 0 else 0

        else:  # NPS
            # NPS calculation: % promoters - % detractors
            nps_responses = responses.filter(question__question_type='nps')
            if nps_responses.exists():
                promoters = nps_responses.filter(numeric_value__gte=9).count()
                detractors = nps_responses.filter(numeric_value__lte=6).count()
                total = nps_responses.count()
                score = ((promoters - detractors) / total * 100) if total > 0 else 0
            else:
                score = 0

        # Update instance
        self.total_score = score
        self.is_negative = score < float(self.survey_template.negative_threshold)
        self.save(update_fields=['total_score', 'is_negative'])

        return score


class SurveyResponse(UUIDModel, TimeStampedModel):
    """
    Survey response - answer to a specific question.
    """
    survey_instance = models.ForeignKey(
        SurveyInstance,
        on_delete=models.CASCADE,
        related_name='responses'
    )
    question = models.ForeignKey(
        SurveyQuestion,
        on_delete=models.PROTECT,
        related_name='responses'
    )

    # Response value (type depends on question type)
    numeric_value = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="For rating, NPS, Likert questions"
    )
    text_value = models.TextField(
        blank=True,
        help_text="For text, textarea questions"
    )
    choice_value = models.CharField(
        max_length=200,
        blank=True,
        help_text="For multiple choice questions"
    )

    # Metadata
    response_time_seconds = models.IntegerField(
        null=True,
        blank=True,
        help_text="Time taken to answer this question"
    )

    class Meta:
        ordering = ['survey_instance', 'question__order']
        unique_together = [['survey_instance', 'question']]

    def __str__(self):
        return f"{self.survey_instance} - {self.question.text[:30]}"
