from django.shortcuts import get_object_or_404
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import BasePermission, IsAuthenticated, SAFE_METHODS
from rest_framework.response import Response


class IsOwnerOrReadPublic(BasePermission):
    """
    Read-only access for anonymous users (public sessions only).
    Authenticated users can only mutate their own sessions.
    """
    def has_permission(self, request, view):
        if request.method in SAFE_METHODS:
            return True
        return request.user.is_authenticated

    def has_object_permission(self, request, view, obj):
        if request.method in SAFE_METHODS:
            return obj.is_public or (
                request.user.is_authenticated and obj.user == request.user
            )
        return request.user.is_authenticated and obj.user == request.user

from .ballistics import compute_corrections
from .models import FreePracticeSession, PRSSession, PRSStage, SpeedShootingSession
from .serializers import (
    FreePracticeSessionDetailSerializer,
    FreePracticeSessionListSerializer,
    FreePracticeSessionWriteSerializer,
    PRSSessionDetailSerializer,
    PRSSessionListSerializer,
    PRSSessionWriteSerializer,
    PRSStageSerializer,
    SpeedShootingSessionDetailSerializer,
    SpeedShootingSessionListSerializer,
    SpeedShootingSessionWriteSerializer,
)


# ── PRS ───────────────────────────────────────────────────────────────────────

class PRSSessionViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadPublic]
    filterset_fields   = ['date', 'is_public']
    search_fields      = ['name', 'location', 'notes', 'competition_name']
    ordering_fields    = ['date', 'created_at']

    def get_queryset(self):
        user = self.request.user
        if not user.is_authenticated:
            qs = PRSSession.objects.filter(is_public=True)
        else:
            qs = PRSSession.objects.filter(user=user)
        qs = qs.select_related('rig', 'ammo', 'reloaded_batch__recipe', 'reloaded_batch__powder', 'analysis')
        if self.action != 'list':
            qs = qs.prefetch_related('stages')
        return qs

    def get_serializer_class(self):
        if self.action == 'list':
            return PRSSessionListSerializer
        if self.action in ('create', 'update', 'partial_update'):
            return PRSSessionWriteSerializer
        return PRSSessionDetailSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    # ── Nested stage actions ──────────────────────────────────────────────────

    @action(detail=True, methods=['get', 'post'], url_path='stages')
    def stages(self, request, pk=None):
        session = self.get_object()
        if request.method == 'GET':
            serializer = PRSStageSerializer(
                session.stages.all(), many=True, context={'request': request}
            )
            return Response(serializer.data)
        serializer = PRSStageSerializer(
            data=request.data,
            context={'request': request, 'session': session},
        )
        serializer.is_valid(raise_exception=True)
        serializer.save(session=session)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @action(detail=True, methods=['get', 'patch', 'delete'],
            url_path=r'stages/(?P<stage_pk>[^/.]+)')
    def stage_detail(self, request, pk=None, stage_pk=None):
        session = self.get_object()
        stage = get_object_or_404(PRSStage, pk=stage_pk, session=session)
        if request.method == 'GET':
            return Response(PRSStageSerializer(stage, context={'request': request}).data)
        if request.method == 'PATCH':
            serializer = PRSStageSerializer(
                stage, data=request.data, partial=True, context={'request': request}
            )
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data)
        stage.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(detail=True, methods=['post'],
            url_path=r'stages/(?P<stage_pk>[^/.]+)/compute-corrections')
    def compute_corrections_action(self, request, pk=None, stage_pk=None):
        session = self.get_object()
        stage = get_object_or_404(PRSStage, pk=stage_pk, session=session)
        return Response(compute_corrections(session, stage))


# ── Free Practice ─────────────────────────────────────────────────────────────

class FreePracticeSessionViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadPublic]
    filterset_fields   = ['date', 'is_public']
    search_fields      = ['name', 'location', 'notes']
    ordering_fields    = ['date', 'created_at']

    def get_queryset(self):
        user = self.request.user
        if not user.is_authenticated:
            return FreePracticeSession.objects.filter(is_public=True).select_related(
                'rig', 'ammo', 'reloaded_batch__recipe', 'reloaded_batch__powder', 'analysis'
            )
        return (
            FreePracticeSession.objects
            .filter(user=user)
            .select_related('rig', 'ammo', 'reloaded_batch__recipe', 'reloaded_batch__powder', 'analysis')
        )

    def get_serializer_class(self):
        if self.action == 'list':
            return FreePracticeSessionListSerializer
        if self.action in ('create', 'update', 'partial_update'):
            return FreePracticeSessionWriteSerializer
        return FreePracticeSessionDetailSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)


# ── Speed Shooting ────────────────────────────────────────────────────────────

class SpeedShootingSessionViewSet(viewsets.ModelViewSet):
    permission_classes = [IsOwnerOrReadPublic]
    filterset_fields   = ['date', 'is_public']
    search_fields      = ['name', 'location', 'notes', 'format']
    ordering_fields    = ['date', 'created_at']

    def get_queryset(self):
        user = self.request.user
        if not user.is_authenticated:
            return SpeedShootingSession.objects.filter(is_public=True).select_related(
                'rig', 'ammo', 'reloaded_batch__recipe', 'reloaded_batch__powder', 'analysis'
            )
        return (
            SpeedShootingSession.objects
            .filter(user=user)
            .select_related('rig', 'ammo', 'reloaded_batch__recipe', 'reloaded_batch__powder', 'analysis')
        )

    def get_serializer_class(self):
        if self.action == 'list':
            return SpeedShootingSessionListSerializer
        if self.action in ('create', 'update', 'partial_update'):
            return SpeedShootingSessionWriteSerializer
        return SpeedShootingSessionDetailSerializer

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)
