"""
Observations views - Public and internal views for observation management.

Public views (no login required):
- observation_create_public: Submit new observation
- observation_submitted: Success page with tracking code
- observation_track: Track observation by code

Internal views (login required):
- observation_list: List all observations with filters
- observation_detail: View observation details
- observation_triage: Triage observation
- observation_change_status: Change observation status
- observation_add_note: Add internal note
- observation_convert_to_action: Convert to PX Action
- category_list: Manage categories
- category_create/edit/delete: Category CRUD
"""
import logging

from django.contrib import messages
from django.contrib.auth.decorators import login_required, permission_required
from django.core.paginator import Paginator
from django.db.models import Q
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import timezone
from django.views.decorators.http import require_http_methods

from apps.accounts.models import User
from apps.organizations.models import Department

from .forms import (
    ConvertToActionForm,
    ObservationCategoryForm,
    ObservationNoteForm,
    ObservationPublicForm,
    ObservationStatusForm,
    ObservationTrackForm,
    ObservationTriageForm,
)
from .models import (
    Observation,
    ObservationAttachment,
    ObservationCategory,
    ObservationNote,
    ObservationStatus,
    ObservationStatusLog,
)
from .services import ObservationService

logger = logging.getLogger(__name__)


# =============================================================================
# PUBLIC VIEWS (No Login Required)
# =============================================================================

def observation_create_public(request):
    """
    Public view for submitting observations.
    
    No login required - anonymous submissions allowed.
    """
    if request.method == 'POST':
        form = ObservationPublicForm(request.POST, request.FILES)
        
        if form.is_valid():
            try:
                # Get client info
                client_ip = get_client_ip(request)
                user_agent = request.META.get('HTTP_USER_AGENT', '')
                
                # Handle file uploads
                attachments = request.FILES.getlist('attachments')
                
                # Create observation using service
                observation = ObservationService.create_observation(
                    description=form.cleaned_data['description'],
                    severity=form.cleaned_data['severity'],
                    category=form.cleaned_data.get('category'),
                    title=form.cleaned_data.get('title', ''),
                    location_text=form.cleaned_data.get('location_text', ''),
                    incident_datetime=form.cleaned_data.get('incident_datetime'),
                    reporter_staff_id=form.cleaned_data.get('reporter_staff_id', ''),
                    reporter_name=form.cleaned_data.get('reporter_name', ''),
                    reporter_phone=form.cleaned_data.get('reporter_phone', ''),
                    reporter_email=form.cleaned_data.get('reporter_email', ''),
                    client_ip=client_ip,
                    user_agent=user_agent,
                    attachments=attachments,
                )
                
                return redirect('observations:observation_submitted', tracking_code=observation.tracking_code)
                
            except Exception as e:
                logger.error(f"Error creating observation: {e}")
                messages.error(request, "An error occurred while submitting your observation. Please try again.")
    else:
        form = ObservationPublicForm()
    
    context = {
        'form': form,
        'categories': ObservationCategory.objects.filter(is_active=True).order_by('sort_order'),
    }
    
    return render(request, 'observations/public_new.html', context)


def observation_submitted(request, tracking_code):
    """
    Success page after observation submission.
    
    Shows tracking code for future reference.
    """
    observation = get_object_or_404(Observation, tracking_code=tracking_code)
    
    context = {
        'observation': observation,
        'tracking_code': tracking_code,
    }
    
    return render(request, 'observations/public_success.html', context)


def observation_track(request):
    """
    Public view to track observation status by tracking code.
    
    Shows minimal status information only (no internal notes).
    """
    observation = None
    form = ObservationTrackForm(request.GET or None)
    
    if request.GET.get('tracking_code'):
        if form.is_valid():
            tracking_code = form.cleaned_data['tracking_code']
            try:
                observation = Observation.objects.get(tracking_code=tracking_code)
            except Observation.DoesNotExist:
                messages.error(request, "No observation found with that tracking code.")
    
    context = {
        'form': form,
        'observation': observation,
    }
    
    return render(request, 'observations/public_track.html', context)


# =============================================================================
# INTERNAL VIEWS (Login Required)
# =============================================================================

@login_required
def observation_list(request):
    """
    Internal view for listing observations with filters.
    
    Features:
    - Server-side pagination
    - Advanced filters (status, severity, category, department, etc.)
    - Search by tracking code, description
    - RBAC filtering
    """
    # Base queryset with optimizations
    queryset = Observation.objects.select_related(
        'category', 'assigned_department', 'assigned_to',
        'triaged_by', 'resolved_by', 'closed_by'
    )
    
    # Apply RBAC filters
    user = request.user
    if user.is_px_admin():
        pass  # See all
    elif user.is_hospital_admin() and user.hospital:
        queryset = queryset.filter(
            Q(assigned_department__hospital=user.hospital) |
            Q(assigned_department__isnull=True)
        )
    elif user.is_department_manager() and user.department:
        queryset = queryset.filter(assigned_department=user.department)
    elif user.hospital:
        queryset = queryset.filter(
            Q(assigned_department__hospital=user.hospital) |
            Q(assigned_department__isnull=True)
        )
    
    # Apply filters from request
    status_filter = request.GET.get('status')
    if status_filter:
        queryset = queryset.filter(status=status_filter)
    
    severity_filter = request.GET.get('severity')
    if severity_filter:
        queryset = queryset.filter(severity=severity_filter)
    
    category_filter = request.GET.get('category')
    if category_filter:
        queryset = queryset.filter(category_id=category_filter)
    
    department_filter = request.GET.get('assigned_department')
    if department_filter:
        queryset = queryset.filter(assigned_department_id=department_filter)
    
    assigned_to_filter = request.GET.get('assigned_to')
    if assigned_to_filter:
        queryset = queryset.filter(assigned_to_id=assigned_to_filter)
    
    is_anonymous_filter = request.GET.get('is_anonymous')
    if is_anonymous_filter == 'yes':
        queryset = queryset.filter(reporter_staff_id='', reporter_name='')
    elif is_anonymous_filter == 'no':
        queryset = queryset.exclude(reporter_staff_id='', reporter_name='')
    
    # Search
    search_query = request.GET.get('search')
    if search_query:
        queryset = queryset.filter(
            Q(tracking_code__icontains=search_query) |
            Q(title__icontains=search_query) |
            Q(description__icontains=search_query) |
            Q(reporter_name__icontains=search_query) |
            Q(reporter_staff_id__icontains=search_query) |
            Q(location_text__icontains=search_query)
        )
    
    # Date range filters
    date_from = request.GET.get('date_from')
    if date_from:
        queryset = queryset.filter(created_at__date__gte=date_from)
    
    date_to = request.GET.get('date_to')
    if date_to:
        queryset = queryset.filter(created_at__date__lte=date_to)
    
    # Ordering
    order_by = request.GET.get('order_by', '-created_at')
    queryset = queryset.order_by(order_by)
    
    # Pagination
    page_size = int(request.GET.get('page_size', 25))
    paginator = Paginator(queryset, page_size)
    page_number = request.GET.get('page', 1)
    page_obj = paginator.get_page(page_number)
    
    # Get filter options
    departments = Department.objects.filter(status='active')
    if not user.is_px_admin() and user.hospital:
        departments = departments.filter(hospital=user.hospital)
    
    assignable_users = User.objects.filter(is_active=True)
    if not user.is_px_admin() and user.hospital:
        assignable_users = assignable_users.filter(hospital=user.hospital)
    
    categories = ObservationCategory.objects.filter(is_active=True)
    
    # Statistics
    stats = ObservationService.get_statistics()
    
    context = {
        'page_obj': page_obj,
        'observations': page_obj.object_list,
        'stats': stats,
        'departments': departments,
        'assignable_users': assignable_users,
        'categories': categories,
        'status_choices': ObservationStatus.choices,
        'filters': request.GET,
    }
    
    return render(request, 'observations/observation_list.html', context)


@login_required
def observation_detail(request, pk):
    """
    Internal view for observation details.
    
    Features:
    - Full observation details
    - Timeline (status logs + notes)
    - Attachments
    - Linked PX Action
    - Workflow actions
    """
    observation = get_object_or_404(
        Observation.objects.select_related(
            'category', 'assigned_department', 'assigned_to',
            'triaged_by', 'resolved_by', 'closed_by'
        ).prefetch_related(
            'attachments',
            'notes__created_by',
            'status_logs__changed_by'
        ),
        pk=pk
    )
    
    # Check access
    user = request.user
    if not user.is_px_admin():
        if user.is_hospital_admin() and observation.assigned_department:
            if observation.assigned_department.hospital != user.hospital:
                messages.error(request, "You don't have permission to view this observation.")
                return redirect('observations:observation_list')
        elif user.is_department_manager() and observation.assigned_department:
            if observation.assigned_department != user.department:
                messages.error(request, "You don't have permission to view this observation.")
                return redirect('observations:observation_list')
    
    # Get timeline (combine status logs and notes)
    status_logs = list(observation.status_logs.all())
    notes = list(observation.notes.all())
    
    timeline = []
    for log in status_logs:
        timeline.append({
            'type': 'status_change',
            'created_at': log.created_at,
            'item': log,
        })
    for note in notes:
        timeline.append({
            'type': 'note',
            'created_at': note.created_at,
            'item': note,
        })
    timeline.sort(key=lambda x: x['created_at'], reverse=True)
    
    # Get attachments
    attachments = observation.attachments.all()
    
    # Get linked PX Action if exists
    px_action = None
    if observation.action_id:
        from apps.px_action_center.models import PXAction
        try:
            px_action = PXAction.objects.get(id=observation.action_id)
        except PXAction.DoesNotExist:
            pass
    
    # Get assignable users and departments
    departments = Department.objects.filter(status='active')
    assignable_users = User.objects.filter(is_active=True)
    if user.hospital:
        departments = departments.filter(hospital=user.hospital)
        assignable_users = assignable_users.filter(hospital=user.hospital)
    
    # Forms
    triage_form = ObservationTriageForm(
        initial={
            'assigned_department': observation.assigned_department,
            'assigned_to': observation.assigned_to,
            'status': observation.status,
        },
        hospital=user.hospital
    )
    status_form = ObservationStatusForm(initial={'status': observation.status})
    note_form = ObservationNoteForm()
    
    context = {
        'observation': observation,
        'timeline': timeline,
        'attachments': attachments,
        'px_action': px_action,
        'departments': departments,
        'assignable_users': assignable_users,
        'triage_form': triage_form,
        'status_form': status_form,
        'note_form': note_form,
        'status_choices': ObservationStatus.choices,
        'can_triage': user.has_perm('observations.triage_observation') or user.is_px_admin(),
        'can_convert': user.is_px_admin() or user.is_hospital_admin(),
    }
    
    return render(request, 'observations/observation_detail.html', context)


@login_required
@require_http_methods(["POST"])
def observation_triage(request, pk):
    """
    Triage an observation - assign department/owner and update status.
    """
    observation = get_object_or_404(Observation, pk=pk)
    
    # Check permission
    user = request.user
    if not (user.has_perm('observations.triage_observation') or user.is_px_admin()):
        messages.error(request, "You don't have permission to triage observations.")
        return redirect('observations:observation_detail', pk=pk)
    
    form = ObservationTriageForm(request.POST, hospital=user.hospital)
    
    if form.is_valid():
        try:
            ObservationService.triage_observation(
                observation=observation,
                triaged_by=user,
                assigned_department=form.cleaned_data.get('assigned_department'),
                assigned_to=form.cleaned_data.get('assigned_to'),
                new_status=form.cleaned_data.get('status'),
                note=form.cleaned_data.get('note', ''),
            )
            messages.success(request, "Observation triaged successfully.")
        except Exception as e:
            logger.error(f"Error triaging observation: {e}")
            messages.error(request, f"Error triaging observation: {str(e)}")
    else:
        messages.error(request, "Invalid form data.")
    
    return redirect('observations:observation_detail', pk=pk)


@login_required
@require_http_methods(["POST"])
def observation_change_status(request, pk):
    """
    Change observation status.
    """
    observation = get_object_or_404(Observation, pk=pk)
    
    # Check permission
    user = request.user
    if not (user.has_perm('observations.triage_observation') or user.is_px_admin()):
        messages.error(request, "You don't have permission to change observation status.")
        return redirect('observations:observation_detail', pk=pk)
    
    form = ObservationStatusForm(request.POST)
    
    if form.is_valid():
        try:
            ObservationService.change_status(
                observation=observation,
                new_status=form.cleaned_data['status'],
                changed_by=user,
                comment=form.cleaned_data.get('comment', ''),
            )
            messages.success(request, f"Status changed to {form.cleaned_data['status']}.")
        except Exception as e:
            logger.error(f"Error changing status: {e}")
            messages.error(request, f"Error changing status: {str(e)}")
    else:
        messages.error(request, "Invalid form data.")
    
    return redirect('observations:observation_detail', pk=pk)


@login_required
@require_http_methods(["POST"])
def observation_add_note(request, pk):
    """
    Add internal note to observation.
    """
    observation = get_object_or_404(Observation, pk=pk)
    
    form = ObservationNoteForm(request.POST)
    
    if form.is_valid():
        try:
            ObservationService.add_note(
                observation=observation,
                note=form.cleaned_data['note'],
                created_by=request.user,
                is_internal=form.cleaned_data.get('is_internal', True),
            )
            messages.success(request, "Note added successfully.")
        except Exception as e:
            logger.error(f"Error adding note: {e}")
            messages.error(request, f"Error adding note: {str(e)}")
    else:
        messages.error(request, "Please enter a note.")
    
    return redirect('observations:observation_detail', pk=pk)


@login_required
@require_http_methods(["GET", "POST"])
def observation_convert_to_action(request, pk):
    """
    Convert observation to PX Action.
    """
    observation = get_object_or_404(Observation, pk=pk)
    
    # Check permission
    user = request.user
    if not (user.is_px_admin() or user.is_hospital_admin()):
        messages.error(request, "You don't have permission to convert observations to actions.")
        return redirect('observations:observation_detail', pk=pk)
    
    # Check if already converted
    if observation.action_id:
        messages.warning(request, "This observation has already been converted to an action.")
        return redirect('observations:observation_detail', pk=pk)
    
    if request.method == 'POST':
        form = ConvertToActionForm(request.POST)
        
        if form.is_valid():
            try:
                action = ObservationService.convert_to_action(
                    observation=observation,
                    created_by=user,
                    title=form.cleaned_data['title'],
                    description=form.cleaned_data['description'],
                    category=form.cleaned_data['category'],
                    priority=form.cleaned_data['priority'],
                    assigned_department=form.cleaned_data.get('assigned_department'),
                    assigned_to=form.cleaned_data.get('assigned_to'),
                )
                messages.success(request, f"Observation converted to action successfully.")
                return redirect('observations:observation_detail', pk=pk)
            except Exception as e:
                logger.error(f"Error converting to action: {e}")
                messages.error(request, f"Error converting to action: {str(e)}")
    else:
        # Pre-populate form
        initial = {
            'title': f"Observation {observation.tracking_code}",
            'description': observation.description,
            'priority': observation.severity,
            'assigned_department': observation.assigned_department,
            'assigned_to': observation.assigned_to,
        }
        if observation.title:
            initial['title'] += f" - {observation.title}"
        elif observation.category:
            initial['title'] += f" - {observation.category.name_en}"
        
        form = ConvertToActionForm(initial=initial)
    
    context = {
        'observation': observation,
        'form': form,
    }
    
    return render(request, 'observations/convert_to_action.html', context)


# =============================================================================
# CATEGORY MANAGEMENT VIEWS
# =============================================================================

@login_required
@permission_required('observations.manage_categories', raise_exception=True)
def category_list(request):
    """
    List observation categories.
    """
    categories = ObservationCategory.objects.all().order_by('sort_order', 'name_en')
    
    context = {
        'categories': categories,
    }
    
    return render(request, 'observations/category_list.html', context)


@login_required
@permission_required('observations.manage_categories', raise_exception=True)
@require_http_methods(["GET", "POST"])
def category_create(request):
    """
    Create new observation category.
    """
    if request.method == 'POST':
        form = ObservationCategoryForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Category created successfully.")
            return redirect('observations:category_list')
    else:
        form = ObservationCategoryForm()
    
    context = {
        'form': form,
        'title': 'Create Category',
    }
    
    return render(request, 'observations/category_form.html', context)


@login_required
@permission_required('observations.manage_categories', raise_exception=True)
@require_http_methods(["GET", "POST"])
def category_edit(request, pk):
    """
    Edit observation category.
    """
    category = get_object_or_404(ObservationCategory, pk=pk)
    
    if request.method == 'POST':
        form = ObservationCategoryForm(request.POST, instance=category)
        if form.is_valid():
            form.save()
            messages.success(request, "Category updated successfully.")
            return redirect('observations:category_list')
    else:
        form = ObservationCategoryForm(instance=category)
    
    context = {
        'form': form,
        'category': category,
        'title': 'Edit Category',
    }
    
    return render(request, 'observations/category_form.html', context)


@login_required
@permission_required('observations.manage_categories', raise_exception=True)
@require_http_methods(["POST"])
def category_delete(request, pk):
    """
    Delete observation category.
    """
    category = get_object_or_404(ObservationCategory, pk=pk)
    
    # Check if category is in use
    if category.observations.exists():
        messages.error(request, "Cannot delete category that is in use.")
        return redirect('observations:category_list')
    
    category.delete()
    messages.success(request, "Category deleted successfully.")
    return redirect('observations:category_list')


# =============================================================================
# AJAX/API HELPERS
# =============================================================================

@login_required
def get_users_by_department(request):
    """
    Get users for a department (AJAX).
    """
    department_id = request.GET.get('department_id')
    if not department_id:
        return JsonResponse({'users': []})
    
    users = User.objects.filter(
        is_active=True,
        department_id=department_id
    ).values('id', 'first_name', 'last_name', 'email')
    
    return JsonResponse({
        'users': [
            {
                'id': str(u['id']),
                'name': f"{u['first_name']} {u['last_name']}",
                'email': u['email'],
            }
            for u in users
        ]
    })


# =============================================================================
# UTILITY FUNCTIONS
# =============================================================================

def get_client_ip(request):
    """
    Get client IP address from request.
    """
    x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
    if x_forwarded_for:
        ip = x_forwarded_for.split(',')[0].strip()
    else:
        ip = request.META.get('REMOTE_ADDR')
    return ip
