"""
Services for moderation functionality.
Following Django styleguide pattern for business logic encapsulation.
"""

from typing import Any

from django.db import transaction
from django.db.models import QuerySet
from django.utils import timezone
from django_fsm import TransitionNotAllowed

from apps.accounts.models import User

from .models import EditSubmission, ModerationQueue, PhotoSubmission


class ModerationService:
    """Service for handling content moderation workflows."""

    @staticmethod
    def approve_submission(*, submission_id: int, moderator: User, notes: str | None = None) -> object | None:
        """
        Approve a content submission and apply changes.

        Args:
            submission_id: ID of the submission to approve
            moderator: User performing the approval
            notes: Optional notes about the approval

        Returns:
            The created/updated object or None if approval failed

        Raises:
            EditSubmission.DoesNotExist: If submission doesn't exist
            ValidationError: If submission data is invalid
            ValueError: If submission cannot be processed
        """
        with transaction.atomic():
            submission = EditSubmission.objects.select_for_update().get(id=submission_id)

            if submission.status != "CLAIMED":
                raise ValueError(f"Submission {submission_id} must be claimed before approval (current status: {submission.status})")

            try:
                # Call the model's approve method which handles the business
                # logic
                obj = submission.approve(moderator)

                # Add moderator notes if provided
                if notes:
                    if submission.notes:
                        submission.notes += f"\n[Moderator]: {notes}"
                    else:
                        submission.notes = f"[Moderator]: {notes}"
                    submission.save()

                return obj

            except Exception as e:
                # Mark as rejected on any error using FSM transition
                try:
                    submission.transition_to_rejected(user=moderator)
                    submission.handled_by = moderator
                    submission.handled_at = timezone.now()
                    submission.notes = f"Approval failed: {str(e)}"
                    submission.save()
                except Exception:
                    # Fallback if FSM transition fails
                    pass
                raise

    @staticmethod
    def reject_submission(*, submission_id: int, moderator: User, reason: str) -> EditSubmission:
        """
        Reject a content submission.

        Args:
            submission_id: ID of the submission to reject
            moderator: User performing the rejection
            reason: Reason for rejection

        Returns:
            Updated submission object

        Raises:
            EditSubmission.DoesNotExist: If submission doesn't exist
            ValueError: If submission cannot be rejected
        """
        with transaction.atomic():
            submission = EditSubmission.objects.select_for_update().get(id=submission_id)

            if submission.status != "CLAIMED":
                raise ValueError(f"Submission {submission_id} must be claimed before rejection (current status: {submission.status})")

            # Use FSM transition method
            submission.transition_to_rejected(user=moderator)
            submission.handled_by = moderator
            submission.handled_at = timezone.now()
            submission.notes = f"Rejected: {reason}"

            # Call full_clean before saving - CRITICAL STYLEGUIDE FIX
            submission.full_clean()
            submission.save()

            return submission

    @staticmethod
    def create_edit_submission(
        *,
        content_object: object,
        changes: dict[str, Any],
        submitter: User,
        submission_type: str = "UPDATE",
        notes: str | None = None,
    ) -> EditSubmission:
        """
        Create a new edit submission for moderation.

        Args:
            content_object: The object being edited
            changes: Dictionary of field changes
            submitter: User submitting the changes
            submission_type: Type of submission ("CREATE" or "UPDATE")
            notes: Optional notes about the submission

        Returns:
            Created EditSubmission object

        Raises:
            ValidationError: If submission data is invalid
        """
        submission = EditSubmission(
            content_object=content_object,
            changes=changes,
            user=submitter,
            submission_type=submission_type,
            reason=notes or "",
        )

        # Call full_clean before saving - CRITICAL STYLEGUIDE FIX
        submission.full_clean()
        submission.save()

        return submission

    @staticmethod
    def update_submission_changes(
        *,
        submission_id: int,
        moderator_changes: dict[str, Any],
        moderator: User,
    ) -> EditSubmission:
        """
        Update submission with moderator changes before approval.

        Args:
            submission_id: ID of the submission to update
            moderator_changes: Dictionary of moderator modifications
            moderator: User making the changes

        Returns:
            Updated submission object

        Raises:
            EditSubmission.DoesNotExist: If submission doesn't exist
            ValueError: If submission cannot be modified
        """
        with transaction.atomic():
            submission = EditSubmission.objects.select_for_update().get(id=submission_id)

            if submission.status not in ("PENDING", "CLAIMED"):
                raise ValueError(f"Submission {submission_id} is not pending or claimed for review")

            submission.moderator_changes = moderator_changes

            # Add note about moderator changes
            note = f"[Moderator changes by {moderator.username}]"
            if submission.notes:
                submission.notes += f"\n{note}"
            else:
                submission.notes = note

            # Call full_clean before saving - CRITICAL STYLEGUIDE FIX
            submission.full_clean()
            submission.save()

            return submission

    @staticmethod
    def get_pending_submissions_for_moderator(
        *,
        moderator: User,
        content_type: str | None = None,
        limit: int | None = None,
    ) -> QuerySet:
        """
        Get pending submissions for a moderator to review.

        Args:
            moderator: The moderator user
            content_type: Optional filter by content type
            limit: Maximum number of submissions to return

        Returns:
            QuerySet of pending submissions
        """
        from .selectors import pending_submissions_for_review

        return pending_submissions_for_review(content_type=content_type, limit=limit)

    @staticmethod
    def get_submission_statistics(*, days: int = 30, moderator: User | None = None) -> dict[str, Any]:
        """
        Get moderation statistics for a time period.

        Args:
            days: Number of days to analyze
            moderator: Optional filter by specific moderator

        Returns:
            Dictionary containing moderation statistics
        """
        from .selectors import moderation_statistics_summary

        return moderation_statistics_summary(days=days, moderator=moderator)

    @staticmethod
    def _is_moderator_or_above(user: User) -> bool:
        """
        Check if user has moderator privileges or above.

        Args:
            user: User to check

        Returns:
            True if user is MODERATOR, ADMIN, or SUPERUSER
        """
        return user.role in ["MODERATOR", "ADMIN", "SUPERUSER"]

    @staticmethod
    def create_edit_submission_with_queue(
        *,
        content_object: object | None,
        changes: dict[str, Any],
        submitter: User,
        submission_type: str = "EDIT",
        reason: str | None = None,
        source: str | None = None,
    ) -> dict[str, Any]:
        """
        Create an edit submission with automatic queue routing.

        For moderators and above: Creates submission and auto-approves
        For regular users: Creates submission and adds to moderation queue

        Args:
            content_object: The object being edited (None for CREATE)
            changes: Dictionary of field changes
            submitter: User submitting the changes
            submission_type: Type of submission ("CREATE" or "EDIT")
            reason: Reason for the submission
            source: Source of information

        Returns:
            Dictionary with submission info and queue status
        """
        with transaction.atomic():
            # Create the submission
            submission = EditSubmission(
                content_object=content_object,
                changes=changes,
                user=submitter,
                submission_type=submission_type,
                reason=reason or "",
                source=source or "",
            )

            submission.full_clean()
            submission.save()

            # Check if user is moderator or above
            if ModerationService._is_moderator_or_above(submitter):
                # Auto-approve for moderators - must claim first then approve
                try:
                    submission.claim(user=submitter)
                    created_object = submission.approve(submitter)
                    return {
                        "submission": submission,
                        "status": "auto_approved",
                        "created_object": created_object,
                        "queue_item": None,
                        "message": "Submission auto-approved for moderator",
                    }
                except Exception as e:
                    return {
                        "submission": submission,
                        "status": "failed",
                        "created_object": None,
                        "queue_item": None,
                        "message": f"Auto-approval failed: {str(e)}",
                    }
            else:
                # Create queue item for regular users
                queue_item = ModerationService._create_queue_item_for_submission(
                    submission=submission, submitter=submitter
                )

                return {
                    "submission": submission,
                    "status": "queued",
                    "created_object": None,
                    "queue_item": queue_item,
                    "message": "Submission added to moderation queue",
                }

    @staticmethod
    def create_photo_submission_with_queue(
        *,
        content_object: object,
        photo,
        caption: str = "",
        date_taken=None,
        submitter: User,
    ) -> dict[str, Any]:
        """
        Create a photo submission with automatic queue routing.

        For moderators and above: Creates submission and auto-approves
        For regular users: Creates submission and adds to moderation queue

        Args:
            content_object: The object the photo is for
            photo: The photo file
            caption: Photo caption
            date_taken: Date the photo was taken
            submitter: User submitting the photo

        Returns:
            Dictionary with submission info and queue status
        """
        with transaction.atomic():
            # Create the photo submission
            submission = PhotoSubmission(
                content_object=content_object,
                photo=photo,
                caption=caption,
                date_taken=date_taken,
                user=submitter,
            )

            submission.full_clean()
            submission.save()

            # Check if user is moderator or above
            if ModerationService._is_moderator_or_above(submitter):
                # Auto-approve for moderators
                try:
                    submission.auto_approve()
                    return {
                        "submission": submission,
                        "status": "auto_approved",
                        "queue_item": None,
                        "message": "Photo submission auto-approved for moderator",
                    }
                except Exception as e:
                    return {
                        "submission": submission,
                        "status": "failed",
                        "queue_item": None,
                        "message": f"Auto-approval failed: {str(e)}",
                    }
            else:
                # Create queue item for regular users
                queue_item = ModerationService._create_queue_item_for_photo_submission(
                    submission=submission, submitter=submitter
                )

                return {
                    "submission": submission,
                    "status": "queued",
                    "queue_item": queue_item,
                    "message": "Photo submission added to moderation queue",
                }

    @staticmethod
    def _create_queue_item_for_submission(*, submission: EditSubmission, submitter: User) -> ModerationQueue:
        """
        Create a moderation queue item for an edit submission.

        Args:
            submission: The edit submission
            submitter: User who made the submission

        Returns:
            Created ModerationQueue item
        """
        # Determine content type and entity info
        content_type = submission.content_type
        entity_type = content_type.model if content_type else "unknown"
        entity_id = submission.object_id

        # Create preview data
        entity_preview = {
            "submission_type": submission.submission_type,
            "changes_count": len(submission.changes) if submission.changes else 0,
            "reason": submission.reason[:100] if submission.reason else "",
        }

        if submission.content_object:
            entity_preview["object_name"] = str(submission.content_object)

        # Determine title and description
        action = "creation" if submission.submission_type == "CREATE" else "edit"
        title = f"{entity_type.title()} {action} by {submitter.username}"

        description = f"Review {action} submission for {entity_type}"
        if submission.reason:
            description += f". Reason: {submission.reason}"

        # Create queue item
        queue_item = ModerationQueue(
            item_type="CONTENT_REVIEW",
            title=title,
            description=description,
            entity_type=entity_type,
            entity_id=entity_id,
            entity_preview=entity_preview,
            content_type=content_type,
            flagged_by=submitter,
            priority="MEDIUM",
            estimated_review_time=15,  # 15 minutes default
            tags=["edit_submission", submission.submission_type.lower()],
        )

        queue_item.full_clean()
        queue_item.save()

        return queue_item

    @staticmethod
    def _create_queue_item_for_photo_submission(*, submission: PhotoSubmission, submitter: User) -> ModerationQueue:
        """
        Create a moderation queue item for a photo submission.

        Args:
            submission: The photo submission
            submitter: User who made the submission

        Returns:
            Created ModerationQueue item
        """
        # Determine content type and entity info
        content_type = submission.content_type
        entity_type = content_type.model if content_type else "unknown"
        entity_id = submission.object_id

        # Create preview data
        entity_preview = {
            "caption": submission.caption,
            "date_taken": submission.date_taken.isoformat() if submission.date_taken else None,
            "photo_url": submission.photo.url if submission.photo else None,
        }

        if submission.content_object:
            entity_preview["object_name"] = str(submission.content_object)

        # Create title and description
        title = f"Photo submission for {entity_type} by {submitter.username}"
        description = f"Review photo submission for {entity_type}"
        if submission.caption:
            description += f". Caption: {submission.caption}"

        # Create queue item
        queue_item = ModerationQueue(
            item_type="CONTENT_REVIEW",
            title=title,
            description=description,
            entity_type=entity_type,
            entity_id=entity_id,
            entity_preview=entity_preview,
            content_type=content_type,
            flagged_by=submitter,
            priority="LOW",  # Photos typically lower priority
            estimated_review_time=5,  # 5 minutes default for photos
            tags=["photo_submission"],
        )

        queue_item.full_clean()
        queue_item.save()

        return queue_item

    @staticmethod
    def process_queue_item(
        *, queue_item_id: int, moderator: User, action: str, notes: str | None = None
    ) -> dict[str, Any]:
        """
        Process a moderation queue item (approve, reject, etc.).

        Args:
            queue_item_id: ID of the queue item to process
            moderator: User processing the item
            action: Action to take ('approve', 'reject', 'escalate')
            notes: Optional notes about the action

        Returns:
            Dictionary with processing results
        """
        with transaction.atomic():
            queue_item = ModerationQueue.objects.select_for_update().get(id=queue_item_id)

            if queue_item.status != "PENDING":
                raise ValueError(f"Queue item {queue_item_id} is not pending")

            # Transition queue item into an active state before processing
            moved_to_in_progress = False
            try:
                queue_item.transition_to_in_progress(user=moderator)
                moved_to_in_progress = True
            except TransitionNotAllowed:
                # If FSM disallows, leave as-is and continue (fallback handled below)
                pass
            except AttributeError:
                # Fallback for environments without the generated transition method
                queue_item.status = "IN_PROGRESS"
                moved_to_in_progress = True

            if moved_to_in_progress:
                queue_item.full_clean()
                queue_item.save()

            def _complete_queue_item() -> None:
                """Transition queue item to completed with FSM-aware fallback."""
                try:
                    queue_item.transition_to_completed(user=moderator)
                except TransitionNotAllowed:
                    queue_item.status = "COMPLETED"
                except AttributeError:
                    queue_item.status = "COMPLETED"

            # Find related submission
            if "edit_submission" in queue_item.tags:
                # Find EditSubmission
                submissions = EditSubmission.objects.filter(
                    user=queue_item.flagged_by,
                    content_type=queue_item.content_type,
                    object_id=queue_item.entity_id,
                    status="PENDING",
                ).order_by("-created_at")

                if not submissions.exists():
                    raise ValueError("No pending edit submission found for this queue item")

                submission = submissions.first()

                if action == "approve":
                    try:
                        created_object = submission.approve(moderator)
                        # Use FSM transition for queue status
                        _complete_queue_item()
                        result = {
                            "status": "approved",
                            "created_object": created_object,
                            "message": "Submission approved successfully",
                        }
                    except Exception as e:
                        # Use FSM transition for queue status
                        _complete_queue_item()
                        result = {"status": "failed", "created_object": None, "message": f"Approval failed: {str(e)}"}
                elif action == "reject":
                    submission.reject(moderator, notes or "Rejected by moderator")
                    # Use FSM transition for queue status
                    _complete_queue_item()
                    result = {"status": "rejected", "created_object": None, "message": "Submission rejected"}
                elif action == "escalate":
                    submission.escalate(moderator, notes or "Escalated for review")
                    queue_item.priority = "HIGH"
                    # Keep status as PENDING for escalation
                    result = {"status": "escalated", "created_object": None, "message": "Submission escalated"}
                else:
                    raise ValueError(f"Unknown action: {action}")

            elif "photo_submission" in queue_item.tags:
                # Find PhotoSubmission
                submissions = PhotoSubmission.objects.filter(
                    user=queue_item.flagged_by,
                    content_type=queue_item.content_type,
                    object_id=queue_item.entity_id,
                    status="PENDING",
                ).order_by("-created_at")

                if not submissions.exists():
                    raise ValueError("No pending photo submission found for this queue item")

                submission = submissions.first()

                if action == "approve":
                    try:
                        submission.approve(moderator, notes or "")
                        # Use FSM transition for queue status
                        _complete_queue_item()
                        result = {
                            "status": "approved",
                            "created_object": None,
                            "message": "Photo submission approved successfully",
                        }
                    except Exception as e:
                        # Use FSM transition for queue status
                        _complete_queue_item()
                        result = {
                            "status": "failed",
                            "created_object": None,
                            "message": f"Photo approval failed: {str(e)}",
                        }
                elif action == "reject":
                    submission.reject(moderator, notes or "Rejected by moderator")
                    # Use FSM transition for queue status
                    _complete_queue_item()
                    result = {"status": "rejected", "created_object": None, "message": "Photo submission rejected"}
                elif action == "escalate":
                    submission.escalate(moderator, notes or "Escalated for review")
                    queue_item.priority = "HIGH"
                    # Keep status as PENDING for escalation
                    result = {"status": "escalated", "created_object": None, "message": "Photo submission escalated"}
                else:
                    raise ValueError(f"Unknown action: {action}")
            else:
                raise ValueError("Unknown queue item type")

            # Update queue item
            queue_item.assigned_to = moderator
            queue_item.assigned_at = timezone.now()
            if notes:
                queue_item.description += f"\n\nModerator notes: {notes}"

            queue_item.full_clean()
            queue_item.save()

            result["queue_item"] = queue_item
            return result
