"""Core Views

This module contains base views, mixins, and common view functionality
used across the entire Adtlas project.
"""

import json
from typing import Any, Dict, List, Optional

from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import QuerySet, Q
from django.http import HttpRequest, HttpResponse, JsonResponse, Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.translation import gettext as _
from django.views import View
from django.views.generic import (
    ListView, DetailView, CreateView, UpdateView, DeleteView,
    TemplateView, FormView
)
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods

from .utils import (
    get_client_ip, get_user_agent, is_ajax_request,
    json_response, success_response, error_response,
    paginate_queryset
)
from .models import ActivityLog


# ============================================================================
# Base Mixins
# ============================================================================

class AjaxResponseMixin:
    """Mixin to handle AJAX requests with JSON responses."""
    
    def dispatch(self, request, *args, **kwargs):
        """Override dispatch to handle AJAX requests."""
        self.is_ajax = is_ajax_request(request)
        return super().dispatch(request, *args, **kwargs)
    
    def form_valid(self, form):
        """Handle valid form submission."""
        response = super().form_valid(form)
        
        if self.is_ajax:
            return self.ajax_form_valid(form)
        
        return response
    
    def form_invalid(self, form):
        """Handle invalid form submission."""
        response = super().form_invalid(form)
        
        if self.is_ajax:
            return self.ajax_form_invalid(form)
        
        return response
    
    def ajax_form_valid(self, form):
        """Handle valid AJAX form submission."""
        return success_response(
            message=_('Operation completed successfully'),
            data={'redirect': self.get_success_url()}
        )
    
    def ajax_form_invalid(self, form):
        """Handle invalid AJAX form submission."""
        errors = {}
        for field, field_errors in form.errors.items():
            errors[field] = field_errors
        
        return error_response(
            message=_('Please correct the errors below'),
            errors=errors
        )


class ActivityLogMixin:
    """Mixin to log user activities."""
    
    activity_type = None
    activity_description = None
    
    def log_activity(self, activity_type=None, description=None, 
                    object_instance=None, extra_data=None):
        """Log user activity."""
        if not hasattr(self.request, 'user') or not self.request.user.is_authenticated:
            return
        
        activity_type = activity_type or self.activity_type
        description = description or self.activity_description
        
        if not activity_type or not description:
            return
        
        # Get object information
        object_type = None
        object_id = None
        
        if object_instance:
            object_type = object_instance.__class__.__name__
            object_id = str(object_instance.pk)
        elif hasattr(self, 'object') and self.object:
            object_type = self.object.__class__.__name__
            object_id = str(self.object.pk)
        
        # Log the activity
        ActivityLog.log_activity(
            user=self.request.user,
            activity_type=activity_type,
            description=description,
            object_type=object_type,
            object_id=object_id,
            ip_address=get_client_ip(self.request),
            user_agent=get_user_agent(self.request),
            extra_data=extra_data or {}
        )
    
    def dispatch(self, request, *args, **kwargs):
        """Override dispatch to log activity."""
        response = super().dispatch(request, *args, **kwargs)
        
        # Log activity after successful request
        if response.status_code < 400:
            self.log_activity()
        
        return response


class PermissionMixin:
    """Mixin to handle permissions with better error messages."""
    
    permission_denied_message = _('You do not have permission to access this page.')
    
    def handle_no_permission(self):
        """Handle permission denied."""
        if self.request.user.is_authenticated:
            if hasattr(self, 'is_ajax') and self.is_ajax:
                return error_response(
                    message=self.permission_denied_message,
                    status=403
                )
            else:
                messages.error(self.request, self.permission_denied_message)
                raise PermissionDenied(self.permission_denied_message)
        
        return super().handle_no_permission()


class SearchMixin:
    """Mixin to add search functionality to list views."""
    
    search_fields = []
    search_param = 'q'
    
    def get_search_query(self):
        """Get search query from request."""
        return self.request.GET.get(self.search_param, '').strip()
    
    def get_queryset(self):
        """Override get_queryset to add search functionality."""
        queryset = super().get_queryset()
        search_query = self.get_search_query()
        
        if search_query and self.search_fields:
            search_filter = Q()
            
            for field in self.search_fields:
                search_filter |= Q(**{f"{field}__icontains": search_query})
            
            queryset = queryset.filter(search_filter)
        
        return queryset
    
    def get_context_data(self, **kwargs):
        """Add search context to template."""
        context = super().get_context_data(**kwargs)
        context['search_query'] = self.get_search_query()
        context['search_fields'] = self.search_fields
        return context


class FilterMixin:
    """Mixin to add filtering functionality to list views."""
    
    filter_fields = {}
    
    def get_filter_params(self):
        """Get filter parameters from request."""
        filters = {}
        
        for param, field in self.filter_fields.items():
            value = self.request.GET.get(param)
            if value:
                filters[field] = value
        
        return filters
    
    def get_queryset(self):
        """Override get_queryset to add filtering."""
        queryset = super().get_queryset()
        filters = self.get_filter_params()
        
        if filters:
            queryset = queryset.filter(**filters)
        
        return queryset
    
    def get_context_data(self, **kwargs):
        """Add filter context to template."""
        context = super().get_context_data(**kwargs)
        context['filter_params'] = self.get_filter_params()
        context['filter_fields'] = self.filter_fields
        return context


class ExportMixin:
    """Mixin to add export functionality to list views."""
    
    export_formats = ['csv', 'excel', 'json']
    export_filename = 'export'
    
    def get_export_queryset(self):
        """Get queryset for export (can be different from display queryset)."""
        return self.get_queryset()
    
    def export_csv(self, queryset):
        """Export queryset as CSV."""
        import csv
        from django.http import HttpResponse
        
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = f'attachment; filename="{self.export_filename}.csv"'
        
        writer = csv.writer(response)
        
        # Write header
        if queryset.exists():
            model = queryset.model
            fields = [field.name for field in model._meta.fields]
            writer.writerow(fields)
            
            # Write data
            for obj in queryset:
                row = [getattr(obj, field, '') for field in fields]
                writer.writerow(row)
        
        return response
    
    def export_json(self, queryset):
        """Export queryset as JSON."""
        from django.core import serializers
        from django.http import HttpResponse
        
        response = HttpResponse(content_type='application/json')
        response['Content-Disposition'] = f'attachment; filename="{self.export_filename}.json"'
        
        data = serializers.serialize('json', queryset)
        response.write(data)
        
        return response
    
    def handle_export(self, format_type):
        """Handle export request."""
        if format_type not in self.export_formats:
            return error_response(message=_('Invalid export format'))
        
        queryset = self.get_export_queryset()
        
        if format_type == 'csv':
            return self.export_csv(queryset)
        elif format_type == 'json':
            return self.export_json(queryset)
        else:
            return error_response(message=_('Export format not implemented'))
    
    def get(self, request, *args, **kwargs):
        """Override get to handle export requests."""
        export_format = request.GET.get('export')
        
        if export_format:
            return self.handle_export(export_format)
        
        return super().get(request, *args, **kwargs)


# ============================================================================
# Base Views
# ============================================================================

class BaseView(AjaxResponseMixin, ActivityLogMixin, PermissionMixin, View):
    """Base view with common functionality."""
    
    def get_context_data(self, **kwargs):
        """Get context data for template."""
        context = kwargs
        context['request'] = self.request
        context['user'] = self.request.user
        return context


class BaseTemplateView(BaseView, TemplateView):
    """Base template view with common functionality."""
    pass


class BaseListView(SearchMixin, FilterMixin, ExportMixin, BaseView, ListView):
    """Base list view with search, filter, and export functionality."""
    
    paginate_by = 25
    
    def get_context_data(self, **kwargs):
        """Add pagination context."""
        context = super().get_context_data(**kwargs)
        
        # Add pagination info
        if context.get('page_obj'):
            page_obj = context['page_obj']
            context.update({
                'total_count': page_obj.paginator.count,
                'start_index': page_obj.start_index(),
                'end_index': page_obj.end_index(),
            })
        
        return context


class BaseDetailView(BaseView, DetailView):
    """Base detail view with common functionality."""
    
    activity_type = 'read'
    
    def get_activity_description(self):
        """Get activity description."""
        return f"Viewed {self.object.__class__.__name__}: {self.object}"
    
    def get_context_data(self, **kwargs):
        """Add object context."""
        context = super().get_context_data(**kwargs)
        self.activity_description = self.get_activity_description()
        return context


class BaseCreateView(BaseView, CreateView):
    """Base create view with common functionality."""
    
    activity_type = 'create'
    
    def get_activity_description(self):
        """Get activity description."""
        return f"Created {self.object.__class__.__name__}: {self.object}"
    
    def form_valid(self, form):
        """Handle valid form submission."""
        response = super().form_valid(form)
        self.activity_description = self.get_activity_description()
        messages.success(self.request, _('Record created successfully.'))
        return response


class BaseUpdateView(BaseView, UpdateView):
    """Base update view with common functionality."""
    
    activity_type = 'update'
    
    def get_activity_description(self):
        """Get activity description."""
        return f"Updated {self.object.__class__.__name__}: {self.object}"
    
    def form_valid(self, form):
        """Handle valid form submission."""
        response = super().form_valid(form)
        self.activity_description = self.get_activity_description()
        messages.success(self.request, _('Record updated successfully.'))
        return response


class BaseDeleteView(BaseView, DeleteView):
    """Base delete view with common functionality."""
    
    activity_type = 'delete'
    
    def get_activity_description(self):
        """Get activity description."""
        return f"Deleted {self.object.__class__.__name__}: {self.object}"
    
    def delete(self, request, *args, **kwargs):
        """Handle delete request."""
        self.object = self.get_object()
        self.activity_description = self.get_activity_description()
        
        response = super().delete(request, *args, **kwargs)
        messages.success(request, _('Record deleted successfully.'))
        
        return response


class BaseFormView(BaseView, FormView):
    """Base form view with common functionality."""
    
    def form_valid(self, form):
        """Handle valid form submission."""
        response = super().form_valid(form)
        messages.success(self.request, _('Form submitted successfully.'))
        return response


# ============================================================================
# API Base Views
# ============================================================================

class BaseAPIView(View):
    """Base API view for handling JSON requests and responses."""
    
    def dispatch(self, request, *args, **kwargs):
        """Override dispatch to handle JSON requests."""
        # Parse JSON data for POST/PUT requests
        if request.content_type == 'application/json':
            try:
                request.json = json.loads(request.body)
            except json.JSONDecodeError:
                return error_response(
                    message=_('Invalid JSON data'),
                    status=400
                )
        else:
            request.json = {}
        
        return super().dispatch(request, *args, **kwargs)
    
    def handle_exception(self, exception):
        """Handle exceptions and return JSON error response."""
        if isinstance(exception, ValidationError):
            return error_response(
                message=str(exception),
                status=400
            )
        elif isinstance(exception, PermissionDenied):
            return error_response(
                message=_('Permission denied'),
                status=403
            )
        elif isinstance(exception, Http404):
            return error_response(
                message=_('Not found'),
                status=404
            )
        else:
            # Log the exception
            import logging
            logger = logging.getLogger(__name__)
            logger.exception(f"API Error: {exception}")
            
            return error_response(
                message=_('Internal server error'),
                status=500
            )


# ============================================================================
# Utility Views
# ============================================================================

@login_required
def dashboard_view(request):
    """Dashboard view for authenticated users."""
    context = {
        'user': request.user,
        'recent_activities': ActivityLog.objects.filter(
            user=request.user
        ).order_by('-created_at')[:10],
    }
    
    return render(request, 'core/dashboard.html', context)


def health_check_view(request):
    """Health check endpoint for monitoring."""
    return JsonResponse({
        'status': 'healthy',
        'timestamp': timezone.now().isoformat(),
        'version': getattr(settings, 'VERSION', '1.0.0')
    })


@require_http_methods(["GET", "POST"])
def ajax_test_view(request):
    """Test view for AJAX functionality."""
    if request.method == 'POST':
        return success_response(
            message=_('AJAX test successful'),
            data={'method': 'POST'}
        )
    
    return success_response(
        message=_('AJAX test successful'),
        data={'method': 'GET'}
    )


class BulkActionView(LoginRequiredMixin, BaseAPIView):
    """Generic view for handling bulk actions."""
    
    model = None
    allowed_actions = []
    
    def post(self, request, *args, **kwargs):
        """Handle bulk action request."""
        action = request.json.get('action')
        object_ids = request.json.get('object_ids', [])
        
        if not action:
            return error_response(message=_('Action is required'))
        
        if action not in self.allowed_actions:
            return error_response(message=_('Invalid action'))
        
        if not object_ids:
            return error_response(message=_('No objects selected'))
        
        # Get objects
        queryset = self.model.objects.filter(id__in=object_ids)
        
        if not queryset.exists():
            return error_response(message=_('No objects found'))
        
        # Perform action
        try:
            result = self.perform_bulk_action(action, queryset)
            
            # Log activity
            ActivityLog.log_activity(
                user=request.user,
                activity_type='bulk_action',
                description=f"Performed bulk {action} on {queryset.count()} {self.model.__name__} objects",
                ip_address=get_client_ip(request),
                user_agent=get_user_agent(request),
                extra_data={
                    'action': action,
                    'object_count': queryset.count(),
                    'object_ids': object_ids
                }
            )
            
            return success_response(
                message=result.get('message', _('Bulk action completed successfully')),
                data=result.get('data', {})
            )
        
        except Exception as e:
            return error_response(message=str(e))
    
    def perform_bulk_action(self, action, queryset):
        """Perform the bulk action. Override in subclasses."""
        if action == 'delete':
            count = queryset.count()
            queryset.delete()
            return {
                'message': f'Deleted {count} objects successfully',
                'data': {'deleted_count': count}
            }
        
        raise NotImplementedError(f"Action '{action}' not implemented")


# ============================================================================
# Error Views
# ============================================================================

def handler404(request, exception):
    """Custom 404 error handler."""
    if is_ajax_request(request):
        return error_response(
            message=_('Page not found'),
            status=404
        )
    
    return render(request, 'errors/404.html', status=404)


def handler500(request):
    """Custom 500 error handler."""
    if is_ajax_request(request):
        return error_response(
            message=_('Internal server error'),
            status=500
        )
    
    return render(request, 'errors/500.html', status=500)


def handler403(request, exception):
    """Custom 403 error handler."""
    if is_ajax_request(request):
        return error_response(
            message=_('Permission denied'),
            status=403
        )
    
    return render(request, 'errors/403.html', status=403)