from ninja import Router, File
from ninja.files import UploadedFile
from ninja.pagination import paginate
from ninja.errors import HttpError
from django.db import transaction
from django.core.files.base import ContentFile
from django.core.cache import cache
from django.utils import timezone
from django.shortcuts import get_object_or_404
from typing import List

from games.decorators import require_game_enabled
from submissions.utils import verify_and_validate_ocr_date_for_submission
from ninja.decorators import decorate_view

from .models import (
    Submission,
    PuzzleResponse,
    SubmissionFile,
    SteamCollectionItem,
    SteamCollection,
)
from .schemas import (
    SubmissionIn,
    SubmissionOut,
    PuzzleResponseOut,
    ValidationIn,
    SteamCollectionItemOut,
    SteamCollectionOut,
)

router = Router()
OPUS_MAGNUM_APP_ID = 558990


@router.get("/puzzles", response=List[SteamCollectionItemOut])
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def list_puzzles(request):
    """Get list of available puzzles"""
    return SteamCollectionItem.objects.select_related("collection").filter(
        collection__is_active=True
    )


@router.get("/collection", response=SteamCollectionOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def get_collection(request):
    """Get the active collection details"""
    collection = get_object_or_404(SteamCollection, is_active=True)
    return collection


@router.get("/submissions", response=List[SubmissionOut])
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
@paginate
def list_submissions(request):
    """Get paginated list of submissions"""
    return Submission.objects.prefetch_related(
        "responses__files", "responses__puzzle"
    ).filter(user=request.user)


@router.get("/submissions/{submission_id}", response=SubmissionOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def get_submission(request, submission_id: str):
    """Get detailed submission by ID"""
    return get_object_or_404(
        Submission.objects.prefetch_related(
            "responses__files", "responses__puzzle"
        ).filter(user=request.user),
        id=submission_id,
    )


@router.post("/submissions", response=SubmissionOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def create_submission(
    request, data: SubmissionIn, files: List[UploadedFile] = File(...)
):
    """Create a new submission with multiple puzzle responses"""

    # Validate that we have files
    if not files:
        return 400, {"detail": "At least one file is required"}

    # Group files by puzzle (based on filename or order)
    # For now, we'll assume files are provided in the same order as responses
    if len(files) < len(data.responses):
        return 400, {"detail": "Not enough files for all responses"}

    # Check if collection is accepting submissions
    if data.responses:
        for puzzle in data.responses:
            if not get_object_or_404(
                SteamCollectionItem, id=puzzle.puzzle_id
            ).collection.accepting_submissions:
                raise HttpError(
                    403, "This tournament is no longer accepting submissions"
                )

    try:
        with transaction.atomic():
            # Check if any confidence score is below 80% to auto-request validation
            auto_request_validation = any(
                (
                    response_data.ocr_confidence_cost is not None
                    and response_data.ocr_confidence_cost < 0.8
                )
                or (
                    response_data.ocr_confidence_cycles is not None
                    and response_data.ocr_confidence_cycles < 0.8
                )
                or (
                    response_data.ocr_confidence_area is not None
                    and response_data.ocr_confidence_area < 0.8
                )
                for response_data in data.responses
            )

            # Create the submission
            submission = Submission.objects.create(
                user=request.user if request.user.is_authenticated else None,
                notes=data.notes,
                manual_validation_requested=data.manual_validation_requested
                or auto_request_validation,
            )

            file_index = 0
            for response_data in data.responses:
                # Get the puzzle
                puzzle = get_object_or_404(
                    SteamCollectionItem, id=response_data.puzzle_id
                )

                # Create the puzzle response
                response = PuzzleResponse.objects.create(
                    submission=submission,
                    puzzle=puzzle,
                    puzzle_name=response_data.puzzle_name,
                    cost=response_data.cost,
                    cycles=response_data.cycles,
                    area=response_data.area,
                    needs_manual_validation=data.manual_validation_requested,
                    ocr_confidence_cost=response_data.ocr_confidence_cost,
                    ocr_confidence_cycles=response_data.ocr_confidence_cycles,
                    ocr_confidence_area=response_data.ocr_confidence_area,
                    **{
                        # Put validated if not manual validation is needed
                        "validated_cost": response_data.cost,
                        "validated_cycles": response_data.cycles,
                        "validated_area": response_data.area,
                    }
                    if not data.manual_validation_requested
                    else {},
                )

                # Process files for this response
                # For simplicity, we'll take one file per response
                # In a real implementation, you'd need better file-to-response mapping
                if file_index < len(files):
                    uploaded_file = files[file_index]

                    # Validate file type
                    if not uploaded_file.content_type.startswith(("image/", "video/")):
                        return 400, {
                            "detail": f"Invalid file type: {uploaded_file.content_type}"
                        }

                    # Validate file size (256MB limit)
                    if uploaded_file.size > 256 * 1024 * 1024:
                        return 400, {"detail": "File too large (max 256MB)"}

                    # Create submission file
                    submission_file = SubmissionFile.objects.create(
                        response=response,
                        original_filename=uploaded_file.name,
                        file_size=uploaded_file.size,
                        content_type=uploaded_file.content_type,
                    )

                    # Save the file
                    submission_file.file.save(
                        uploaded_file.name, ContentFile(uploaded_file.read()), save=True
                    )

                    file_index += 1

                # Check if OCR validation is needed
                if not all(
                    [response_data.cost, response_data.cycles, response_data.area]
                ):
                    response.mark_for_validation("Incomplete OCR data")

            # Reload with relations for response
            submission = Submission.objects.prefetch_related(
                "responses__files", "responses__puzzle"
            ).get(id=submission.id)

            # Invalidate results cache on successful submission
            cache.delete("api:results:results")

            return submission

    except Exception as e:
        return 500, {"detail": f"Error creating submission: {str(e)}"}


@router.put("/responses/{response_id}/validate", response=PuzzleResponseOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def validate_response(request, response_id: int, data: ValidationIn):
    """Manually validate a puzzle response"""

    if not request.user.is_authenticated or not request.user.is_staff:
        return 403, {"detail": "Admin access required"}

    response = get_object_or_404(PuzzleResponse, id=response_id)

    if data.puzzle is not None:
        puzzle = get_object_or_404(SteamCollectionItem, id=data.puzzle)
        response.puzzle = puzzle

    # Update validated values
    if data.validated_cost is not None:
        response.validated_cost = data.validated_cost

    if data.validated_cycles is not None:
        response.validated_cycles = data.validated_cycles

    if data.validated_area is not None:
        response.validated_area = data.validated_area

    # Mark as no longer needing validation if we have all values
    if all([response.final_cost, response.final_cycles, response.final_area]):
        response.needs_manual_validation = False

    response.save()

    # Invalidate results cache when a response is validated
    cache.delete("api:results:results")

    return response


@router.put("/responses/{response_id}/validate/auto", response=PuzzleResponseOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def validate_auto(request, response_id: int):
    """Try to auto validate a puzzle response"""

    if not request.user.is_authenticated or not request.user.is_staff:
        return 403, {"detail": "Admin access required"}

    response = get_object_or_404(PuzzleResponse, id=response_id)

    for file in response.files.all():
        verify_and_validate_ocr_date_for_submission(file)

    return response


@router.get("/responses/needs-validation", response=List[PuzzleResponseOut])
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def list_responses_needing_validation(request):
    """Get all responses that need manual validation"""

    if not request.user.is_authenticated or not request.user.is_staff:
        return 403, {"detail": "Admin access required"}

    return (
        PuzzleResponse.objects.filter(needs_manual_validation=True)
        .filter(puzzle__collection__is_active=True)
        .select_related("puzzle", "submission")
        .prefetch_related("files")
    )


@router.post("/submissions/{submission_id}/validate", response=SubmissionOut)
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def validate_submission(request, submission_id: str):
    """Mark entire submission as validated"""

    if not request.user.is_authenticated or not request.user.is_staff:
        return 403, {"detail": "Admin access required"}

    submission = get_object_or_404(Submission, id=submission_id)

    submission.is_validated = True
    submission.validated_by = request.user
    submission.validated_at = timezone.now()
    submission.save()

    # Also mark all responses as not needing validation
    submission.responses.update(needs_manual_validation=False)

    # Reload with relations
    submission = Submission.objects.prefetch_related(
        "responses__files", "responses__puzzle"
    ).get(id=submission.id)

    # Invalidate results cache when submission is validated
    cache.delete("api:results:results")

    return submission


@router.delete("/submissions/{submission_id}")
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def delete_submission(request, submission_id: str):
    """Delete a submission (admin only)"""

    if not request.user.is_authenticated or not request.user.is_staff:
        return 403, {"detail": "Admin access required"}

    submission = get_object_or_404(Submission, id=submission_id)
    submission.delete()

    # Invalidate results cache when submission is deleted
    cache.delete("api:results:results")

    return {"detail": "Submission deleted successfully"}


@router.get("/stats")
@decorate_view(require_game_enabled(OPUS_MAGNUM_APP_ID))
def get_stats(request):
    """Get submission statistics"""

    total_submissions = Submission.objects.count()
    total_responses = PuzzleResponse.objects.count()
    needs_validation = PuzzleResponse.objects.filter(
        needs_manual_validation=True
    ).count()
    validated_submissions = Submission.objects.filter(is_validated=True).count()

    return {
        "total_submissions": total_submissions,
        "total_responses": total_responses,
        "needs_validation": needs_validation,
        "validated_submissions": validated_submissions,
        "validation_rate": (total_responses - needs_validation) / total_responses
        if total_responses
        else 0,
    }
