"""
Simulator views for testing external notification APIs.

This module provides API endpoints that:
- Simulate external email and SMS services
- Receive and process HIS journey events
- Create journeys, send surveys, and trigger notifications
- Generate fake HIS visit data for testing
"""

import logging
import random
from datetime import datetime, timedelta
from django.conf import settings
from django.core.mail import send_mail
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
import json

from .serializers import HISJourneyEventSerializer, HISJourneyEventListSerializer
from .models import HISRequestLog
from apps.journeys.models import (
    JourneyType,
    PatientJourneyTemplate,
    PatientJourneyInstance,
    PatientJourneyStageInstance,
    StageStatus,
)
from apps.organizations.models import Hospital, Department, Patient
from apps.surveys.models import SurveyTemplate, SurveyInstance
from apps.notifications.services import NotificationService
import time


def make_json_serializable(data):
    """
    Convert datetime objects to ISO strings for JSON serialization.

    Recursively processes dictionaries and lists to ensure all datetime
    objects are converted to ISO format strings for storage in JSON fields.

    Args:
        data: Data structure that may contain datetime objects

    Returns:
        Data structure with datetime objects converted to ISO strings
    """
    if isinstance(data, dict):
        return {k: make_json_serializable(v) for k, v in data.items()}
    elif isinstance(data, list):
        return [make_json_serializable(item) for item in data]
    elif isinstance(data, datetime):
        return data.isoformat()
    else:
        return data


logger = logging.getLogger(__name__)

# Request counter for tracking
request_counter = {"email": 0, "sms": 0}

# Request history (last 10 requests)
request_history = []


def log_simulator_request(
    channel, payload, status, response_data=None, error_message=None, processing_time_ms=None, request=None
):
    """
    Log simulator request to history, file, and database.

    Args:
        channel: Channel type (email, sms, his_event)
        payload: Request payload data
        status: Request status (success, failed, partial, sent)
        response_data: Response data from simulator
        error_message: Error message if failed
        processing_time_ms: Processing time in milliseconds
        request: HTTP request object for IP/user agent
    """
    # Log to in-memory history (keeps last 10)
    request_id = len(request_history) + 1
    entry = {
        "id": request_id,
        "channel": channel,
        "timestamp": datetime.now().isoformat(),
        "status": status,
        "payload": payload,
    }
    request_history.append(entry)

    if len(request_history) > 10:
        request_history.pop(0)

    # Log to file
    logger.info(f"[Simulator] {channel.upper()} Request #{request_id}: {status}")

    # Log to database
    try:
        # Convert datetime objects to ISO strings for JSON serialization
        serializable_response_data = make_json_serializable(response_data) if response_data else None

        log_entry = HISRequestLog(
            channel=channel,
            payload=payload,
            status=status,
            response_data=serializable_response_data,
            processing_time_ms=processing_time_ms,
            error_message=error_message,
            ip_address=request.META.get("REMOTE_ADDR") if request else None,
            user_agent=request.META.get("HTTP_USER_AGENT") if request else None,
        )

        # Extract channel-specific fields
        if channel == "email":
            log_entry.recipient = payload.get("to")
            log_entry.subject = payload.get("subject")
            message = payload.get("message", "")
            log_entry.message_preview = message[:500] if message else None
        elif channel == "sms":
            log_entry.recipient = payload.get("to")
            message = payload.get("message", "")
            log_entry.message_preview = message[:500] if message else None
        elif channel == "his_event":
            # Extract from first event if it's a list
            events = payload.get("events", [])
            if events and isinstance(events, list) and len(events) > 0:
                first_event = events[0]
                log_entry.patient_id = first_event.get("mrn")
                log_entry.journey_id = first_event.get("encounter_id")
                log_entry.hospital_code = first_event.get("hospital_code")
                log_entry.event_type = first_event.get("event_type")
                log_entry.visit_type = first_event.get("visit_type")
                log_entry.department = first_event.get("department")

        log_entry.save()
        logger.info(f"[Simulator] Logged to database with ID: {log_entry.request_id}")

    except Exception as e:
        logger.error(f"[Simulator] Failed to log to database: {str(e)}")


@csrf_exempt
@require_http_methods(["POST"])
def email_simulator(request):
    """
    Simulate external email API endpoint.

    Accepts POST request with JSON payload:
    {
        "to": "recipient@example.com",
        "subject": "Email subject",
        "message": "Plain text message",
        "html_message": "Optional HTML content"
    }

    Sends real email via Django SMTP and returns 200 OK.
    """
    request_counter["email"] += 1
    start_time = time.time()

    try:
        # Parse request body
        data = json.loads(request.body)

        # Validate required fields
        required_fields = ["to", "subject", "message"]
        missing_fields = [field for field in required_fields if field not in data]

        if missing_fields:
            processing_time = int((time.time() - start_time) * 1000)
            response = {"success": False, "error": f"Missing required fields: {', '.join(missing_fields)}"}
            log_simulator_request(
                "email",
                data,
                "failed",
                response_data=response,
                error_message=response["error"],
                processing_time_ms=processing_time,
                request=request,
            )
            return JsonResponse(response, status=400)

        # Extract fields
        to_email = data["to"]
        subject = data["subject"]
        message = data["message"]
        html_message = data.get("html_message", None)

        # Log the request
        logger.info(f"[Email Simulator] Sending email to {to_email}: {subject}")

        # Print formatted email to terminal
        print(f"\n{'╔' + '═' * 68 + '╗'}")
        print(f"║{' ' * 15}📧 EMAIL SIMULATOR{' ' * 34}║")
        print(f"╠{'═' * 68}╣")
        print(f"║ Request #: {request_counter['email']:<52} ║")
        print(f"╠{'═' * 68}╣")
        print(f"║ To:      {to_email:<52} ║")
        print(f"║ Subject: {subject[:52]:<52} ║")
        print(f"╠{'═' * 68}╣")
        print(f"║ Message:{' ' * 55} ║")

        # Word wrap message
        words = message.split()
        line = "║         "
        for word in words:
            if len(line) + len(word) + 1 > 68:
                print(f"{line:<68} ║")
                line = "║         " + word
            else:
                if line == "║         ":
                    line += word
                else:
                    line += " " + word

        # Print last line if not empty
        if line != "║         ":
            print(f"{line:<68} ║")

        # Print HTML section if present
        if html_message:
            print(f"╠{'═' * 68}╣")
            print(f"║ HTML Message:{' ' * 48} ║")
            html_preview = html_message[:200].replace("\n", " ")
            print(f"║ {html_preview:<66} ║")
            if len(html_message) > 200:
                print(f"║ ... ({len(html_message)} total characters){' ' * 30} ║")

        print(f"╚{'═' * 68}╝\n")

        # Send real email via Django SMTP
        try:
            send_mail(
                subject=subject,
                message=message,
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[to_email],
                html_message=html_message,
                fail_silently=False,
            )
            logger.info(f"[Email Simulator] Email sent via SMTP to {to_email}")
            email_status = "sent"
        except Exception as email_error:
            logger.error(f"[Email Simulator] SMTP Error: {str(email_error)}")
            # Log as 'partial' since we displayed it but failed to send
            email_status = "partial"
            processing_time = int((time.time() - start_time) * 1000)
            # Return error response
            response = {
                "success": False,
                "error": f"Email displayed but failed to send via SMTP: {str(email_error)}",
                "data": {
                    "to": to_email,
                    "subject": subject,
                    "message_length": len(message),
                    "has_html": html_message is not None,
                    "displayed": True,
                    "sent": False,
                },
            }
            log_simulator_request(
                "email",
                data,
                email_status,
                response_data=response,
                error_message=response["error"],
                processing_time_ms=processing_time,
                request=request,
            )
            return JsonResponse(response, status=500)

        processing_time = int((time.time() - start_time) * 1000)
        # Log success
        logger.info(f"[Email Simulator] Email sent successfully to {to_email}")

        response = {
            "success": True,
            "message": "Email sent successfully",
            "data": {
                "to": to_email,
                "subject": subject,
                "message_length": len(message),
                "has_html": html_message is not None,
            },
        }

        log_simulator_request(
            "email", data, email_status, response_data=response, processing_time_ms=processing_time, request=request
        )

        return JsonResponse(response, status=200)

    except json.JSONDecodeError:
        processing_time = int((time.time() - start_time) * 1000)
        response = {"success": False, "error": "Invalid JSON format"}
        log_simulator_request(
            "email",
            {},
            "failed",
            response_data=response,
            error_message=response["error"],
            processing_time_ms=processing_time,
            request=request,
        )
        return JsonResponse(response, status=400)

    except Exception as e:
        processing_time = int((time.time() - start_time) * 1000)
        logger.error(f"[Email Simulator] Error: {str(e)}")
        response = {"success": False, "error": str(e)}
        log_simulator_request(
            "email",
            data if "data" in locals() else {},
            "failed",
            response_data=response,
            error_message=str(e),
            processing_time_ms=processing_time,
            request=request,
        )
        return JsonResponse(response, status=500)


@csrf_exempt
@require_http_methods(["POST"])
def sms_simulator(request):
    """
    Simulate external SMS API endpoint.

    Accepts POST request with JSON payload:
    {
        "to": "+966501234567",
        "message": "SMS message text"
    }

    Prints SMS to terminal with formatted output and returns 200 OK.
    """
    request_counter["sms"] += 1
    start_time = time.time()

    try:
        # Parse request body
        data = json.loads(request.body)

        # Validate required fields
        required_fields = ["to", "message"]
        missing_fields = [field for field in required_fields if field not in data]

        if missing_fields:
            processing_time = int((time.time() - start_time) * 1000)
            response = {"success": False, "error": f"Missing required fields: {', '.join(missing_fields)}"}
            log_simulator_request(
                "sms",
                data,
                "failed",
                response_data=response,
                error_message=response["error"],
                processing_time_ms=processing_time,
                request=request,
            )
            return JsonResponse(response, status=400)

        # Extract fields
        to_phone = data["to"]
        message = data["message"]
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # Log the request
        logger.info(f"[SMS Simulator] Sending SMS to {to_phone}")

        # Print formatted SMS to terminal
        print(f"\n{'╔' + '═' * 68 + '╗'}")
        print(f"║{' ' * 15}📱 SMS SIMULATOR{' ' * 35}║")
        print(f"╠{'═' * 68}╣")
        print(f"║ Request #: {request_counter['sms']:<52} ║")
        print(f"╠{'═' * 68}╣")
        print(f"║ To:      {to_phone:<52} ║")
        print(f"║ Time:    {timestamp:<52} ║")
        print(f"╠{'═' * 68}╣")
        print(f"║ Message:{' ' * 55} ║")

        # Word wrap message
        words = message.split()
        line = "║         "
        for word in words:
            if len(line) + len(word) + 1 > 68:
                print(f"{line:<68} ║")
                line = "║         " + word
            else:
                if line == "║         ":
                    line += word
                else:
                    line += " " + word

        # Print last line if not empty
        if line != "║         ":
            print(f"{line:<68} ║")

        print(f"╚{'═' * 68}╝\n")

        processing_time = int((time.time() - start_time) * 1000)
        # Log success
        logger.info(f"[SMS Simulator] SMS sent to {to_phone}: {message[:50]}...")

        response = {
            "success": True,
            "message": "SMS sent successfully",
            "data": {"to": to_phone, "message_length": len(message)},
        }

        log_simulator_request(
            "sms", data, "sent", response_data=response, processing_time_ms=processing_time, request=request
        )

        return JsonResponse(response, status=200)

    except json.JSONDecodeError:
        processing_time = int((time.time() - start_time) * 1000)
        response = {"success": False, "error": "Invalid JSON format"}
        log_simulator_request(
            "sms",
            {},
            "failed",
            response_data=response,
            error_message=response["error"],
            processing_time_ms=processing_time,
            request=request,
        )
        return JsonResponse(response, status=400)

    except Exception as e:
        processing_time = int((time.time() - start_time) * 1000)
        logger.error(f"[SMS Simulator] Error: {str(e)}")
        response = {"success": False, "error": str(e)}
        log_simulator_request(
            "sms",
            data if "data" in locals() else {},
            "failed",
            response_data=response,
            error_message=str(e),
            processing_time_ms=processing_time,
            request=request,
        )
        return JsonResponse(response, status=500)


@csrf_exempt
@require_http_methods(["GET"])
def health_check(request):
    """
    Health check endpoint for simulator.

    Returns simulator status and statistics.
    """
    return JsonResponse(
        {
            "status": "healthy",
            "timestamp": datetime.now().isoformat(),
            "statistics": {
                "total_requests": request_counter["email"] + request_counter["sms"],
                "email_requests": request_counter["email"],
                "sms_requests": request_counter["sms"],
            },
            "recent_requests": request_history[-5:],  # Last 5 requests
        },
        status=200,
    )


@csrf_exempt
@require_http_methods(["GET"])
def reset_simulator(request):
    """
    Reset simulator statistics and history.

    Clears request counter and history.
    """
    global request_counter, request_history
    request_counter = {"email": 0, "sms": 0}
    request_history = []

    logger.info("[Simulator] Reset statistics and history")

    return JsonResponse({"success": True, "message": "Simulator reset successfully"}, status=200)


from rest_framework.permissions import AllowAny
from rest_framework.decorators import permission_classes

# Import HIS adapter and serializers
from apps.integrations.services.his_adapter import HISAdapter
from apps.integrations.serializers import HISPatientDataSerializer


@api_view(["POST"])
@permission_classes([AllowAny])
def his_patient_data_handler(request):
    """
    HIS Patient Data API Endpoint - Real HIS Format

    Receives patient data from HIS simulator in real HIS format:
    - Creates or updates patient records
    - Creates journey instances with stage completions
    - Sends post-discharge surveys when patient is discharged

    Expected payload (real HIS format):
    {
        "FetchPatientDataTimeStampList": [{
            "Type": "Patient Demographic details",
            "PatientID": "123456",
            "AdmissionID": "789012",
            "HospitalID": "1",
            "HospitalName": "ALH-main",
            "AdmitDate": "05-Jun-2025 11:06",
            "DischargeDate": "05-Jun-2025 15:30",
            "SSN": "1234567890",
            "PatientName": "Ahmed Mohammed",
            "MobileNo": "0501234567",
            ...
        }],
        "FetchPatientDataTimeStampVisitDataList": [
            {"Type": "Consultation", "BillDate": "05-Jun-2025 11:06"},
            {"Type": "Doctor Visited", "BillDate": "05-Jun-2025 11:20"},
            ...
        ],
        "Code": 200,
        "Status": "Success"
    }
    """
    start_time = time.time()

    try:
        # Validate request data using HIS serializer
        # print("---------------------------------------------")
        serializer = HISPatientDataSerializer(data=request.data)
        if not serializer.is_valid():
            processing_time = int((time.time() - start_time) * 1000)
            return Response(
                {"error": "Invalid HIS data format", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST
            )

        # Process HIS data using adapter
        result = HISAdapter.process_his_data(serializer.validated_data)

        processing_time = int((time.time() - start_time) * 1000)

        if not result["success"]:
            # Log the failed request
            log_simulator_request(
                "his_event",
                request.data,
                "failed",
                response_data={"error": result["message"]},
                error_message=result["message"],
                processing_time_ms=processing_time,
                request=request,
            )

            return Response({"error": result["message"]}, status=status.HTTP_400_BAD_REQUEST)

        # Prepare success response - only survey-related data (no journey data)
        response_data = {
            "success": True,
            "message": result["message"],
            "patient_id": str(result["patient"].id) if result["patient"] else None,
            "patient_mrn": result["patient"].mrn if result["patient"] else None,
            "patient_name": result["patient"].get_full_name() if result["patient"] else None,
            "patient_type": result.get("patient_type"),
            "survey_id": str(result["survey"].id) if result["survey"] else None,
            "survey_sent": result["survey_sent"],
            "survey_url": result["survey_url"],
        }

        # Log the successful request
        log_simulator_request(
            "his_event",
            request.data,
            "success",
            response_data=response_data,
            processing_time_ms=processing_time,
            request=request,
        )

        return Response(response_data, status=status.HTTP_200_OK)

    except Exception as e:
        processing_time = int((time.time() - start_time) * 1000)
        logger.error(f"[HIS Patient Data Handler] Error: {str(e)}", exc_info=True)

        # Log the failed request
        log_simulator_request(
            "his_event",
            request.data,
            "failed",
            response_data={"error": str(e)},
            error_message=str(e),
            processing_time_ms=processing_time,
            request=request,
        )

        return Response(
            {"error": "Internal server error", "details": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(["POST"])
@permission_classes([AllowAny])
def his_events_handler(request):
    """
    HIS Events API Endpoint - Legacy Format

    Receives patient journey events from HIS simulator:
    - Creates or updates patient records
    - Creates journey instances
    - Processes stage completions
    - Sends post-discharge surveys when journey is complete

    Expected payload:
    {
        "events": [
            {
                "encounter_id": "ENC-2024-001",
                "mrn": "MRN-12345",
                "national_id": "1234567890",
                "first_name": "Ahmed",
                "last_name": "Mohammed",
                "phone": "+966501234567",
                "email": "patient@example.com",
                "event_type": "OPD_STAGE_1_REGISTRATION",
                "timestamp": "2024-01-20T10:30:00Z",
                "visit_type": "opd",
                "department": "Cardiology",
                "hospital_code": "ALH-main"
            }
        ]
    }

    NOTE: This endpoint is kept for backward compatibility.
    New integrations should use /api/simulator/his-patient-data/
    """
    start_time = time.time()

    try:
        # Validate request data
        serializer = HISJourneyEventListSerializer(data=request.data)
        if not serializer.is_valid():
            return Response({"error": "Invalid data", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)

        events_data = serializer.validated_data["events"]

        # Process each event
        results = []
        survey_invitations_sent = []

        for event_data in events_data:
            result = process_his_event(event_data)
            results.append(result)

            # Track if survey was sent
            if result.get("survey_sent"):
                survey_invitations_sent.append(
                    {
                        "encounter_id": event_data["encounter_id"],
                        "survey_id": result["survey_id"],
                        "survey_url": result["survey_url"],
                    }
                )

        processing_time = int((time.time() - start_time) * 1000)

        response_data = {
            "success": True,
            "message": f"Processed {len(events_data)} events successfully",
            "results": results,
            "surveys_sent": len(survey_invitations_sent),
            "survey_details": survey_invitations_sent,
        }

        # Log the successful request
        log_simulator_request(
            "his_event",
            request.data,
            "success",
            response_data=response_data,
            processing_time_ms=processing_time,
            request=request,
        )

        return Response(response_data, status=status.HTTP_200_OK)

    except Exception as e:
        processing_time = int((time.time() - start_time) * 1000)
        logger.error(f"[HIS Events Handler] Error: {str(e)}", exc_info=True)

        # Log the failed request
        log_simulator_request(
            "his_event",
            request.data,
            "failed",
            response_data={"error": str(e)},
            error_message=str(e),
            processing_time_ms=processing_time,
            request=request,
        )

        return Response(
            {"error": "Internal server error", "details": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


def process_his_event(event_data):
    """
    Process a single HIS journey event

    Steps:
    1. Get or create patient
    2. Get or create journey instance
    3. Find and update stage instance
    4. Check if journey is complete and send survey if needed
    """
    try:
        # 1. Get or create patient
        patient = get_or_create_patient(event_data)

        # 2. Get or create journey instance
        journey_instance = get_or_create_journey_instance(event_data, patient)

        # 3. Find and update stage instance
        stage_instance = update_stage_instance(journey_instance, event_data)

        result = {
            "encounter_id": event_data["encounter_id"],
            "patient_id": str(patient.id) if patient else None,
            "journey_id": str(journey_instance.id),
            "stage_id": str(stage_instance.id) if stage_instance else None,
            "stage_status": stage_instance.status if stage_instance else "not_found",
            "survey_sent": False,
        }

        # 4. Check if journey is complete and send survey
        if journey_instance.is_complete():
            survey_result = send_post_discharge_survey(journey_instance, patient)
            if survey_result:
                result.update(survey_result)

        return result

    except Exception as e:
        logger.error(
            f"[Process HIS Event] Error for encounter {event_data.get('encounter_id')}: {str(e)}", exc_info=True
        )
        return {"encounter_id": event_data.get("encounter_id"), "error": str(e), "success": False}


def get_or_create_patient(event_data):
    """Get or create patient from event data"""
    try:
        patient, created = Patient.objects.get_or_create(
            mrn=event_data["mrn"],
            defaults={
                "first_name": event_data["first_name"],
                "last_name": event_data["last_name"],
                "phone": event_data["phone"],
                "email": event_data["email"],
                "national_id": event_data.get("national_id", ""),
            },
        )
        logger.info(f"{'Created' if created else 'Found'} patient {patient.mrn}: {patient.get_full_name()}")
        return patient
    except Exception as e:
        logger.error(f"Error creating patient: {str(e)}")
        raise


def get_or_create_journey_instance(event_data, patient):
    """Get or create journey instance for this encounter"""
    try:
        # Get hospital from event data or default to ALH-main
        hospital_code = event_data.get("hospital_code", "ALH-main")
        hospital = Hospital.objects.filter(code=hospital_code).first()
        if not hospital:
            raise ValueError(
                f"Hospital with code '{hospital_code}' not found. Please run seed_journey_surveys command first."
            )

        # Map visit_type to JourneyType
        journey_type_map = {"ems": JourneyType.EMS, "inpatient": JourneyType.INPATIENT, "opd": JourneyType.OPD}
        journey_type = journey_type_map.get(event_data["visit_type"])

        if not journey_type:
            raise ValueError(f"Invalid visit_type: {event_data['visit_type']}")

        # Get journey template
        journey_template = PatientJourneyTemplate.objects.filter(
            hospital=hospital, journey_type=journey_type, is_active=True
        ).first()

        if not journey_template:
            raise ValueError(f"No active journey template found for {journey_type}")

        # Get or create journey instance
        journey_instance, created = PatientJourneyInstance.objects.get_or_create(
            encounter_id=event_data["encounter_id"],
            defaults={
                "journey_template": journey_template,
                "patient": patient,
                "hospital": hospital,
                "status": "active",
            },
        )

        # Create stage instances if this is a new journey
        if created:
            for stage_template in journey_template.stages.filter(is_active=True):
                PatientJourneyStageInstance.objects.create(
                    journey_instance=journey_instance, stage_template=stage_template, status=StageStatus.PENDING
                )
            logger.info(
                f"Created new journey instance {journey_instance.id} with {journey_template.stages.count()} stages"
            )

        return journey_instance

    except Exception as e:
        logger.error(f"Error creating journey instance: {str(e)}")
        raise


def update_stage_instance(journey_instance, event_data):
    """Find and update stage instance based on event_type"""
    try:
        # Find stage template by trigger_event_code
        stage_template = journey_instance.journey_template.stages.filter(
            trigger_event_code=event_data["event_type"], is_active=True
        ).first()

        if not stage_template:
            logger.warning(f"No stage template found for event_type: {event_data['event_type']}")
            return None

        # Get or create stage instance
        stage_instance, created = PatientJourneyStageInstance.objects.get_or_create(
            journey_instance=journey_instance, stage_template=stage_template, defaults={"status": StageStatus.PENDING}
        )

        # Complete the stage
        if stage_instance.status != StageStatus.COMPLETED:
            from django.utils import timezone

            stage_instance.status = StageStatus.COMPLETED
            stage_instance.completed_at = timezone.now()
            stage_instance.save()
            logger.info(f"Completed stage {stage_template.name} for journey {journey_instance.encounter_id}")
        else:
            logger.info(f"Stage {stage_template.name} already completed for journey {journey_instance.encounter_id}")

        return stage_instance

    except Exception as e:
        logger.error(f"Error updating stage instance: {str(e)}")
        raise


def send_post_discharge_survey(journey_instance, patient):
    """
    Send post-discharge survey to patient

    Creates a survey instance and sends invitation via email and SMS
    """
    try:
        # Check if journey template has post-discharge survey enabled
        journey_template = journey_instance.journey_template
        if not journey_template.send_post_discharge_survey:
            return None

        # Check if survey already sent for this journey
        existing_survey = SurveyInstance.objects.filter(journey_instance=journey_instance).first()

        if existing_survey:
            logger.info(f"Survey already sent for journey {journey_instance.encounter_id}")
            return None

        # Get survey template from journey template
        # Use first stage's survey template as the comprehensive survey
        first_stage = journey_template.stages.filter(is_active=True).order_by("order").first()
        if not first_stage or not first_stage.survey_template:
            logger.warning(f"No survey template found for journey {journey_instance.encounter_id}")
            return None

        survey_template = first_stage.survey_template

        # Create survey instance
        survey_instance = SurveyInstance.objects.create(
            survey_template=survey_template,
            patient=patient,
            journey_instance=journey_instance,
            hospital=journey_instance.hospital,
            delivery_channel="email",  # Primary channel is email
            status="pending",
            recipient_email=patient.email,
            recipient_phone=patient.phone,
        )

        logger.info(f"Created survey instance {survey_instance.id} for journey {journey_instance.encounter_id}")

        # Send survey invitation via email
        try:
            email_log = NotificationService.send_survey_invitation(survey_instance, language="en")
            logger.info(f"Survey invitation sent via email to {patient.email}")
        except Exception as e:
            logger.error(f"Error sending survey email: {str(e)}")

        # Also send via SMS (as backup)
        try:
            sms_log = NotificationService.send_sms(
                phone=patient.phone,
                message=f"Your experience survey is ready: {survey_instance.get_survey_url()}",
                related_object=survey_instance,
                metadata={"survey_id": str(survey_instance.id)},
            )
            logger.info(f"Survey invitation sent via SMS to {patient.phone}")
        except Exception as e:
            logger.error(f"Error sending survey SMS: {str(e)}")

        # Return survey details
        return {
            "survey_sent": True,
            "survey_id": str(survey_instance.id),
            "survey_url": survey_instance.get_survey_url(),
            "delivery_channel": "email_and_sms",
        }

    except Exception as e:
        logger.error(f"Error sending post-discharge survey: {str(e)}", exc_info=True)
        return {"survey_sent": False, "error": str(e)}


HIS_HOSPITALS = {
    "2": {"name": "SUWAIDI", "id": "2"},
    "3": {"name": "NUZHA", "id": "3"},
}

ARABIC_FIRST_NAMES_MALE = [
    "Mohammed",
    "Ahmed",
    "Abdullah",
    "Omar",
    "Ali",
    "Saud",
    "Fahad",
    "Turki",
    "Khalid",
    "Youssef",
    "Abdulrahman",
    "Abdulaziz",
    "Nasser",
    "Majid",
    "Ibrahim",
    "Hassan",
    "Saleh",
    "Faisal",
    "Saeed",
    "Walid",
    "Naif",
    "Mishaal",
    "Abdullah",
    "Bader",
    "Sultan",
]

ARABIC_FIRST_NAMES_FEMALE = [
    "Fatima",
    "Aisha",
    "Sarah",
    "Nora",
    "Layla",
    "Hessa",
    "Reem",
    "Mona",
    "Dalal",
    "Jawaher",
    "Maryam",
    "Ghaida",
    "Shaima",
    "Suha",
    "Fahdah",
    "Amwaj",
    "Nagwa",
    "Fadiya",
    "Afnan",
    "Hailah",
    "Meznah",
    "Manar",
    "Ghala",
    "Sharifah",
    "Inayah",
    "Aljuri",
]

ARABIC_LAST_NAMES = [
    "Alharbi",
    "Alotaibi",
    "Alshammari",
    "Aldosari",
    "Almutairi",
    "Alqahtani",
    "Alzahrani",
    "Alghamdi",
    "Alsubaie",
    "Alanazi",
    "Almalki",
    "Alahmari",
    "Aldkhini",
    "Albakhet",
    "Alangari",
    "Almas",
    "Bu Subayt",
    "Alkathiri",
    "Al Dimshaq",
    "Alhumaid",
    "Alshaddadi",
    "Altambakti",
    "Abunemeh",
    "Iqbal",
    "Khan",
    "Mustafa",
    "Munir",
    "Abdelghafar",
    "Ba Saad",
    "Abounada",
    "Jamur",
    "Alasmri",
    "Badawoud",
    "Allaboun",
    "Alsuobie",
]

NATIONALITIES = [
    ("SAUDI", 55),
    ("PAKISTAN", 10),
    ("JORDAN", 8),
    ("YEMEN", 7),
    ("EGYPT", 5),
    ("SYRIA", 3),
    ("SUDAN", 3),
    ("NIGERIA", 2),
    ("PALESTINIAN EGYPTIAN", 2),
    ("PALESTINIAN", 2),
    ("INDIAN", 2),
    ("FILIPINO", 1),
]

INSURANCE_COMPANIES = [
    "The Cooperative Company for Cooperative Insurance",
    "Bupa Arabia for Cooperative Insurance",
    "The Mediterranean and Gulf Cooperative Insurance and Reinsurance Company (MedGulf)",
    "Al-Rajhi Company for Cooperative Insurance",
    "Saudi United Cooperative Insurance Company (Wala)",
    "Center for National Health Insurance",
    "Arabian Shield Cooperative Insurance Company",
    "Tawuniya Cooperative Insurance Company",
]

COMPANIES_GRADES = [
    ("Rasan Al Arabiya Information Technology // TAWUNIYA", "GL/A"),
    ("Walaa CoOperative Insurance Co // Walaa - GlobeMed", "A"),
    ("NATIONAL WATER COMPANY / TAWUNIYA", "VIP"),
    ("BUREAU OF EXPERTS AT THE C OF MINISTERS // TAWUNIYA", "VVIP"),
    ("M/S SFDA // MEDGULF", "VIP"),
    ("IHP MEDTRONIC // BUPA", "VIP"),
    ("SAVETO LTD // MEDGULF", "A"),
    ("IHP - WSP MIDDLE EAST AND PARTNER SAUDI COMPANY // BUPA", "VIP"),
    (
        "Al Hammadi for Mgmt / Pol # 1054568804, 805, 806, 807, 808, 809, 810, 811, 829, 837, 1054572893 - Al Rajhi Takaful",
        "A",
    ),
    ("Wons Beverage Establishment // AL Rajhi", "GOLD 5.5"),
    ("Watani Iron Steel Company // AL RAJHI", "A"),
    ("MAJID AL FUTTAIM FASHION SPC // NCCI", "B"),
    ("IHP - WSP MIDDLE EAST AND PARTNER SAUDI COMPANY // BUPA", "VIP"),
    ("SAUDI PORTS AUTHORITY // TAWUNIYA", "VIP"),
    ("SAUDI ARAMCO // BUPA", "GENERAL CLASS"),
    ("CNHI (Center for National Health Insurance)", "GENERAL CLASS"),
    ("OPERATIONS ALLIANCE OPS O AND M COMPANY // BUPA", "GOLD"),
    ("ELITE LAW FIRM AND L //TAWUNIYA", "GL/B"),
    ("Dynamic Solutions For General Contracting Est // Al Rajhi", "A"),
    ("NATIONAL SECURITY SERVICES COMPANY SAFE // BUPA", "VIP"),
    ("Dhuruma O  M Company Limited // Bupa", "A"),
    ("Al Ayuni Investment and Contracting Branch Co. (2) // BUPA", "B"),
    ("AlFuttaim First Commercial Company // BUPA", "SILVER"),
    ("International Human Resources / Tawuniya", "A"),
    ("HULOOL ALSAHABA FOR IT AND COMMUNICATION// Tawuniya", "A"),
    ("Hisham Nabil Ali for contracting Co // BUPA", "PREMIUM 2.1 S"),
    ("YOUSSEF MARROUN CONT CO // NCCI", "B"),
    ("BALSAM 10", "GENERAL - MAX. SR. 50"),
    ("BANK ALBILAD // BUPA", "VIP"),
    ("Al Hammadi for Mgmt / Pol # 1054568804 - Al Rajhi Takaful", "A"),
]

DOCTORS = [
    ("12524", "MAI ABOELNASR ELBORI"),
    ("16468", "HEBA ELSHABOURY ABDELATTY"),
    ("18816", "ABDELMONEIM  MOHAMED GAMIL  IBRAHIM"),
    ("8663", "RAZETHA MOHAMMED IBRAHIM"),
    ("13383", "ABDELRHMAN SAAD ELAMIN"),
    ("15091", "MOHAMAD AMAR MOHAMAD YASER MSALAM"),
    ("15289", "HASSAN ASHRAF AHMED"),
    ("12055", "SAMEH SAEED ABDELFATTAH"),
    ("8902", "SHADI FAWAZ AL MAGRABI"),
    ("14761", "DOAA AWAD MASSOUD"),
    ("6092", "ADEL ABDELTAWAB ELSHENAWY"),
    ("8606", "AHMED NABIL YAKOUT"),
    ("18829", "ATHEEL  SALEH H  ALDBASSI"),
    ("13031", "ABDULAZIZ ALANAZI"),
    ("13524", "BAHA TOHAMY DIAB"),
    ("16764", "WALID MOHAMED ABDELSATTAR"),
    ("2957", "AHMED ALBORAEY KABEEL"),
    ("18189", "SAMER MOUHAMED HAMADEH"),
    ("18176", "ELAWAD KHALID OMER NAFIE"),
    ("15453", "EMAN HAMDY MAHMOUD ABDELRAHIM"),
    ("18884", "EMAN  ABDELMAGEED  BASIONY ELHAMRAWY"),
    ("13454", "HAGIR ASIM HUSSEIN"),
    ("4904", "ABDULRAHMAN ABBAS ALMUTAWAKEL"),
    ("11259", "WALID MOHAMED ABDELSATTAR"),
    ("6824", "MOHAMMED MAISARA ABDULHAMID"),
    ("4873", "ORWA MOHAMED HAMICH"),
    ("12461", "ELAWAD KHALID OMER NAFIE"),
    ("9922", "HASSAN ASHRAF AHMED"),
    ("8484", "ABDELRHMAN SAAD ELAMIN"),
    ("13010", "ABDELMONEIM  MOHAMED GAMIL  IBRAHIM"),
]

HIS_VISIT_EVENT_TYPES = [
    "Consultation",
    "Doctor Visited",
    "Clinical Condtion",
    "ChiefComplaint",
    "Prescribed Drugs",
]


def _weighted_choice(choices_with_weights):
    total = sum(w for _, w in choices_with_weights)
    r = random.uniform(0, total)
    cumulative = 0
    for choice, weight in choices_with_weights:
        cumulative += weight
        if r <= cumulative:
            return choice
    return choices_with_weights[0][0]


def _format_his_date(dt):
    months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
    return f"{dt.day:02d}-{months[dt.month - 1]}-{dt.year} {dt.hour:02d}:{dt.minute:02d}"


def _generate_name(gender):
    if gender == "Male":
        first = random.choice(ARABIC_FIRST_NAMES_MALE)
    else:
        first = random.choice(ARABIC_FIRST_NAMES_FEMALE)
    middle_options = [
        "Mohammed",
        "Ahmed",
        "Ibrahim",
        "Abdullah",
        "Saleh",
        "Husam",
        "Saad",
        "Fahad",
        "Mutaz",
        "Othman",
        "Abdullatif",
        "Yousef",
        "",
    ]
    middle = random.choice(middle_options)
    last = random.choice(ARABIC_LAST_NAMES)
    if middle:
        full_name = f"{first} {middle}  {last}"
    else:
        full_name = f"{first} {last}"
    return full_name


def _generate_age(dob):
    today = datetime.now()
    years = today.year - dob.year
    months = today.month - dob.month
    if months < 0:
        years -= 1
        months += 12
    if years >= 1:
        if years == 1:
            return f"1 Year(s)"
        return f"{years} Year(s)"
    else:
        return f"{months} Month(s)"


def _generate_patient_data(hospital_id, hospital_name, patient_type_code):
    patient_id = str(random.randint(2000000, 2999999))
    admission_id = str(random.randint(811900, 899999))
    reg_code = f"ALHH.{random.randint(100000000, 999999999)}"

    is_male = random.random() < 0.45
    gender = "Male" if is_male else "Female"
    gender_id = "1" if is_male else "2"
    patient_name = _generate_name(gender)

    dob = datetime.now() - timedelta(days=random.randint(60, 95 * 365))
    full_age = _generate_age(dob)

    nationality = _weighted_choice(NATIONALITIES)

    ssn = "".join([str(random.randint(0, 9)) for _ in range(10)])
    if nationality not in ["SAUDI", "JORDAN", "YEMEN", "EGYPT", "SYRIA", "SUDAN"]:
        if random.random() < 0.3:
            ssn = f"P{random.randint(10000000, 99999999)}"

    phone = f"05{random.randint(0, 9)}{random.randint(10000000, 99999999)}"

    doctor_id, doctor_name = random.choice(DOCTORS)
    consultant_id = str(random.randint(100, 9999))

    admit_date = datetime.now() - timedelta(
        days=random.randint(0, 6),
        hours=random.randint(0, 23),
        minutes=random.randint(0, 59),
    )

    has_insurance = random.random() < 0.65
    if has_insurance:
        company_name, grade_name = random.choice(COMPANIES_GRADES)
        company_id = str(random.randint(10000, 99999))
        grade_id = str(random.randint(1, 9999))
        insurance_company = random.choice(INSURANCE_COMPANIES)
        bill_type = "CR"
    else:
        company_name = ""
        grade_name = ""
        company_id = ""
        grade_id = "0"
        insurance_company = ""
        bill_type = "CS"

    discharge_date = None
    if patient_type_code == "ED":
        if random.random() < 0.80:
            hours = random.randint(0, 6)
            discharge_date = admit_date + timedelta(hours=max(1, hours), minutes=random.randint(0, 59))
    elif patient_type_code == "IP":
        if random.random() < 0.50:
            days = random.randint(1, 5)
            discharge_date = admit_date + timedelta(days=days, hours=random.randint(0, 14))
    elif patient_type_code == "OP":
        if random.random() < 0.60:
            hours = random.randint(1, 3)
            discharge_date = admit_date + timedelta(hours=hours, minutes=random.randint(0, 59))

    return (
        {
            "Type": "Patient Demographic details",
            "PatientID": patient_id,
            "AdmissionID": admission_id,
            "HospitalID": hospital_id,
            "HospitalName": hospital_name,
            "PatientTypeID": patient_type_code,
            "PatientType": patient_type_code,
            "AdmitDate": _format_his_date(admit_date),
            "DischargeDate": _format_his_date(discharge_date) if discharge_date else None,
            "RegCode": reg_code,
            "SSN": ssn,
            "PatientName": patient_name,
            "GenderID": gender_id,
            "Gender": gender,
            "FullAge": full_age,
            "PatientNationality": nationality,
            "MobileNo": phone,
            "DOB": _format_his_date(dob),
            "ConsultantID": consultant_id,
            "PrimaryDoctor": f"{doctor_id}-{doctor_name}",
            "CompanyID": company_id,
            "GradeID": grade_id,
            "CompanyName": company_name,
            "GradeName": grade_name,
            "InsuranceCompanyName": insurance_company,
            "BillType": bill_type,
            "IsVIP": "0",
        },
        admit_date,
        discharge_date,
    )


def _generate_visit_timeline(
    admit_date, discharge_date, patient_type_code, patient_id, admission_id, reg_code, ssn, mobile_no
):
    visit_data = []
    is_discharged = discharge_date is not None

    if patient_type_code == "ED":
        if is_discharged:
            num_events = random.randint(3, 5)
        else:
            num_events = random.randint(1, 3)
    elif patient_type_code == "IP":
        if is_discharged:
            num_events = random.randint(3, 5)
        else:
            num_events = random.randint(1, 3)
    else:
        num_events = random.randint(1, 2)

    selected_types = random.sample(HIS_VISIT_EVENT_TYPES, min(num_events, len(HIS_VISIT_EVENT_TYPES)))

    base_time = admit_date + timedelta(minutes=random.randint(15, 45))
    for i, event_type in enumerate(selected_types):
        event_time = base_time + timedelta(
            minutes=i * random.randint(10, 30),
            seconds=random.randint(0, 59),
        )
        visit_data.append(
            {
                "Type": event_type,
                "BillDate": _format_his_date(event_time),
                "PatientID": patient_id,
                "PatientType": patient_type_code,
                "AdmissionID": admission_id,
                "RegCode": reg_code,
                "SSN": ssn,
                "MobileNo": mobile_no,
            }
        )

    return visit_data


@api_view(["GET"])
@permission_classes([AllowAny])
def generate_visit_data(request):
    """
    Generate fake HIS visit data matching the real HIS format (visit_data.json).

    GET /api/simulator/generate-visit/?patient_type=ED&hospital_id=2

    Query Parameters:
        patient_type: "ED", "IP", or "OP" (default: random with realistic distribution)
        hospital_id: "2" (SUWAIDI) or "3" (NUZHA) (default: random)

    Returns a single fake patient record in the exact HIS response format.
    """
    patient_type_param = request.GET.get("patient_type", "").upper()
    hospital_id_param = request.GET.get("hospital_id", "")

    if patient_type_param in ("ED", "IP", "OP"):
        patient_type_code = patient_type_param
    else:
        r = random.random()
        if r < 0.55:
            patient_type_code = "ED"
        elif r < 0.80:
            patient_type_code = "IP"
        else:
            patient_type_code = "OP"

    if hospital_id_param in HIS_HOSPITALS:
        hospital = HIS_HOSPITALS[hospital_id_param]
    else:
        hospital = random.choice(list(HIS_HOSPITALS.values()))

    hospital_id = hospital["id"]
    hospital_name = hospital["name"]

    patient_data, admit_date, discharge_date = _generate_patient_data(hospital_id, hospital_name, patient_type_code)

    visit_data = _generate_visit_timeline(
        admit_date,
        discharge_date,
        patient_type_code,
        patient_data["PatientID"],
        patient_data["AdmissionID"],
        patient_data["RegCode"],
        patient_data["SSN"],
        patient_data["MobileNo"],
    )

    response = {
        "FetchPatientDataTimeStampList": [patient_data],
        "FetchPatientDataTimeStampVisitDataList": visit_data,
        "FetchPatientDataTimeStampVisitEDDataList": visit_data if patient_type_code == "ED" else [],
        "FetchPatientDataTimeStampVisitIPDataList": visit_data if patient_type_code == "IP" else [],
        "FetchPatientDataTimeStampVisitOPDataList": visit_data if patient_type_code == "OP" else [],
        "Code": 200,
        "Status": "Success",
        "Message": "",
        "Message2L": "",
        "MobileNo": "",
        "ValidateMessage": "",
    }

    return Response(response)
