import json
from typing import Any, cast

from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import UserPassesTestMixin
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.http import (
    HttpRequest,
    HttpResponse,
    HttpResponseForbidden,
    JsonResponse,
)
from django.views.generic import DetailView

from .models import EditSubmission, PhotoSubmission, UserType

User = get_user_model()


class EditSubmissionMixin(DetailView):
    """
    Mixin for handling edit submissions with proper moderation.
    """

    model: type[models.Model] | None = None

    def handle_edit_submission(
        self,
        request: HttpRequest,
        changes: dict[str, Any],
        reason: str = "",
        source: str = "",
        submission_type: str = "EDIT",
    ) -> JsonResponse:
        """
        Handle an edit submission based on user's role.

        Args:
            request: The HTTP request
            changes: Dict of field changes {field_name: new_value}
            reason: Why this edit is needed
            source: Source of information (optional)
            submission_type: 'EDIT' or 'CREATE'

        Returns:
            JsonResponse with status and message
        """
        if not request.user.is_authenticated:
            return JsonResponse(
                {
                    "status": "error",
                    "message": "You must be logged in to make edits.",
                },
                status=403,
            )

        if not self.model:
            raise ValueError("model attribute must be set")

        content_type = ContentType.objects.get_for_model(self.model)

        # Create the submission
        submission = EditSubmission(
            user=request.user,
            content_type=content_type,
            submission_type=submission_type,
            changes=changes,
            reason=reason,
            source=source,
        )

        # For edits, set the object_id
        if submission_type == "EDIT":
            obj = self.get_object()
            submission.object_id = getattr(obj, "id", None)

        # Auto-approve for moderators and above
        user_role = getattr(request.user, "role", None)
        if user_role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
            obj = submission.approve(cast(UserType, request.user))
            return JsonResponse(
                {
                    "status": "success",
                    "message": "Changes saved successfully.",
                    "auto_approved": True,
                    "redirect_url": getattr(obj, "get_absolute_url", lambda: None)(),
                }
            )

        # Submit for approval for regular users
        submission.save()
        return JsonResponse(
            {
                "status": "success",
                "message": "Your changes have been submitted for approval.",
                "auto_approved": False,
            }
        )

    def post(self, request: HttpRequest, *args: Any, **kwargs: Any) -> JsonResponse:
        """Handle POST requests for editing"""
        if not request.user.is_authenticated:
            return JsonResponse(
                {
                    "status": "error",
                    "message": "You must be logged in to make edits.",
                },
                status=403,
            )

        try:
            data = json.loads(request.body)
            changes = data.get("changes", {})
            reason = data.get("reason", "")
            source = data.get("source", "")
            submission_type = data.get("submission_type", "EDIT")

            if not changes:
                return JsonResponse(
                    {"status": "error", "message": "No changes provided."},
                    status=400,
                )

            user_role = getattr(request.user, "role", None)
            if not reason and user_role == "USER":
                return JsonResponse(
                    {
                        "status": "error",
                        "message": "Please provide a reason for your changes.",
                    },
                    status=400,
                )

            return self.handle_edit_submission(request, changes, reason, source, submission_type)

        except json.JSONDecodeError:
            return JsonResponse(
                {"status": "error", "message": "Invalid JSON data."},
                status=400,
            )
        except Exception as e:
            return JsonResponse({"status": "error", "message": str(e)}, status=500)


class PhotoSubmissionMixin(DetailView):
    """
    Mixin for handling photo submissions with proper moderation.
    """

    model: type[models.Model] | None = None

    def handle_photo_submission(self, request: HttpRequest) -> JsonResponse:
        """Handle a photo submission based on user's role"""
        if not request.user.is_authenticated:
            return JsonResponse(
                {
                    "status": "error",
                    "message": "You must be logged in to upload photos.",
                },
                status=403,
            )

        if not self.model:
            raise ValueError("model attribute must be set")

        try:
            obj = self.get_object()
        except (AttributeError, self.model.DoesNotExist):
            return JsonResponse({"status": "error", "message": "Invalid object."}, status=400)

        if not request.FILES.get("photo"):
            return JsonResponse(
                {"status": "error", "message": "No photo provided."},
                status=400,
            )

        content_type = ContentType.objects.get_for_model(obj)

        submission = PhotoSubmission(
            user=request.user,
            content_type=content_type,
            object_id=getattr(obj, "id", None),
            photo=request.FILES["photo"],
            caption=request.POST.get("caption", ""),
            date_taken=request.POST.get("date_taken"),
        )

        # Auto-approve for moderators and above
        user_role = getattr(request.user, "role", None)
        if user_role in ["MODERATOR", "ADMIN", "SUPERUSER"]:
            submission.auto_approve()
            return JsonResponse(
                {
                    "status": "success",
                    "message": "Photo uploaded successfully.",
                    "auto_approved": True,
                }
            )

        # Submit for approval for regular users
        submission.save()
        return JsonResponse(
            {
                "status": "success",
                "message": "Your photo has been submitted for approval.",
                "auto_approved": False,
            }
        )


class ModeratorRequiredMixin(UserPassesTestMixin):
    """Require moderator or higher role for access"""

    request: HttpRequest | None = None

    def test_func(self) -> bool:
        if not self.request:
            return False
        user_role = getattr(self.request.user, "role", None)
        return self.request.user.is_authenticated and user_role in [
            "MODERATOR",
            "ADMIN",
            "SUPERUSER",
        ]

    def handle_no_permission(self) -> HttpResponse:
        if not self.request or not self.request.user.is_authenticated:
            return super().handle_no_permission()
        return HttpResponseForbidden("You must be a moderator to access this page.")


class AdminRequiredMixin(UserPassesTestMixin):
    """Require admin or superuser role for access"""

    request: HttpRequest | None = None

    def test_func(self) -> bool:
        if not self.request:
            return False
        user_role = getattr(self.request.user, "role", None)
        return self.request.user.is_authenticated and user_role in [
            "ADMIN",
            "SUPERUSER",
        ]

    def handle_no_permission(self) -> HttpResponse:
        if not self.request or not self.request.user.is_authenticated:
            return super().handle_no_permission()
        return HttpResponseForbidden("You must be an admin to access this page.")


class InlineEditMixin:
    """Add inline editing context to views"""

    request: HttpRequest | None = None

    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)  # type: ignore
        if self.request and self.request.user.is_authenticated:
            context["can_edit"] = True
            user_role = getattr(self.request.user, "role", None)
            context["can_auto_approve"] = user_role in [
                "MODERATOR",
                "ADMIN",
                "SUPERUSER",
            ]

            if isinstance(self, DetailView):
                obj = self.get_object()  # type: ignore
                context["pending_edits"] = (
                    EditSubmission.objects.filter(
                        content_type=ContentType.objects.get_for_model(obj.__class__),
                        object_id=getattr(obj, "id", None),
                        status="NEW",
                    )
                    .select_related("user")
                    .order_by("-created_at")
                )
        return context


class HistoryMixin:
    """Add edit history context to views"""

    def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
        context = super().get_context_data(**kwargs)  # type: ignore

        # Only add history context for DetailViews
        if isinstance(self, DetailView):
            obj = self.get_object()  # type: ignore

            # Get historical records ordered by date if available
            try:
                # Use pghistory's get_history method
                context["history"] = obj.get_history()
            except (AttributeError, TypeError):
                context["history"] = []

            # Get related edit submissions
            content_type = ContentType.objects.get_for_model(obj.__class__)
            context["edit_submissions"] = (
                EditSubmission.objects.filter(
                    content_type=content_type,
                    object_id=getattr(obj, "id", None),
                )
                .exclude(status="NEW")
                .select_related("user", "handled_by")
                .order_by("-created_at")
            )

        return context
