from django.shortcuts import get_object_or_404
from django.utils import timezone
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from rest_framework.response import Response

from .models import (
    Ammo,
    Bipod,
    Brass,
    Bullet,
    Firearm,
    GearStatus,
    Magazine,
    Powder,
    Primer,
    ReloadedAmmoBatch,
    ReloadRecipe,
    Rig,
    RigItem,
    Scope,
    Suppressor,
    UserGear,
)
from .permissions import IsAdminOrReadOnly
from .serializers import (
    AmmoSerializer,
    BipodSerializer,
    BrassSerializer,
    BulletSerializer,
    FirearmSerializer,
    MagazineSerializer,
    PowderSerializer,
    PrimerSerializer,
    ReloadedAmmoBatchSerializer,
    ReloadRecipeSerializer,
    RigItemCreateSerializer,
    RigItemReadSerializer,
    RigSerializer,
    ScopeSerializer,
    SuppressorSerializer,
    UserGearSerializer,
)


# ── Gear catalog — shared mixin ───────────────────────────────────────────────

class GearCatalogMixin:
    """
    Behaviour shared across all per-type gear viewsets.

    - GET list/retrieve: authenticated users see only VERIFIED entries;
      staff see everything.
    - POST: any authenticated user may submit a new entry (status=PENDING).
      Staff submissions are auto-verified.
    - PUT/PATCH/DELETE: staff only.
    - POST .../verify/ or .../reject/: staff only.
    """

    def get_queryset(self):
        from django.db.models import Q
        if self.request.user.is_staff:
            return self.queryset.all()
        return self.queryset.filter(
            Q(status=GearStatus.VERIFIED) |
            Q(status=GearStatus.PENDING, submitted_by=self.request.user)
        )

    def get_permissions(self):
        if self.action in ('update', 'partial_update', 'destroy'):
            return [IsAdminUser()]
        return [IsAuthenticated()]

    def perform_create(self, serializer):
        if self.request.user.is_staff:
            serializer.save(
                status=GearStatus.VERIFIED,
                reviewed_by=self.request.user,
                reviewed_at=timezone.now(),
            )
        else:
            serializer.save(
                status=GearStatus.PENDING,
                submitted_by=self.request.user,
            )

    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def verify(self, request, pk=None):
        gear = self.get_object()
        gear.verify(reviewed_by=request.user)
        return Response(self.get_serializer(gear).data)

    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def reject(self, request, pk=None):
        gear = self.get_object()
        gear.reject(reviewed_by=request.user)
        return Response(self.get_serializer(gear).data)


# ── Per-type gear viewsets ────────────────────────────────────────────────────

class FirearmViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    queryset = Firearm.objects.select_related('submitted_by', 'reviewed_by', 'caliber')
    serializer_class = FirearmSerializer
    search_fields = ['brand', 'model_name', 'caliber__name']
    ordering_fields = ['brand', 'model_name', 'caliber__name', 'created_at']
    filterset_fields = ['firearm_type', 'caliber', 'status']


class ScopeViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    queryset = Scope.objects.select_related('submitted_by', 'reviewed_by')
    serializer_class = ScopeSerializer
    search_fields = ['brand', 'model_name', 'reticle_type']
    ordering_fields = ['brand', 'model_name', 'magnification_max', 'created_at']
    filterset_fields = ['reticle_type', 'status']


class SuppressorViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    queryset = Suppressor.objects.select_related('submitted_by', 'reviewed_by', 'max_caliber')
    serializer_class = SuppressorSerializer
    search_fields = ['brand', 'model_name', 'max_caliber__name']
    ordering_fields = ['brand', 'model_name', 'created_at']
    filterset_fields = ['max_caliber', 'status']


class BipodViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    queryset = Bipod.objects.select_related('submitted_by', 'reviewed_by')
    serializer_class = BipodSerializer
    search_fields = ['brand', 'model_name']
    ordering_fields = ['brand', 'model_name', 'created_at']
    filterset_fields = ['attachment_type', 'status']


class MagazineViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    queryset = Magazine.objects.select_related('submitted_by', 'reviewed_by', 'caliber')
    serializer_class = MagazineSerializer
    search_fields = ['brand', 'model_name', 'caliber__name']
    ordering_fields = ['brand', 'model_name', 'caliber__name', 'created_at']
    filterset_fields = ['caliber', 'status']


# ── User inventory ────────────────────────────────────────────────────────────

class UserGearViewSet(viewsets.ModelViewSet):
    """
    The authenticated user's personal gear inventory.
    Each item links a catalog Gear to the user with optional personal metadata.
    """
    serializer_class = UserGearSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = None
    search_fields = ['nickname', 'serial_number', 'gear__brand', 'gear__model_name']
    ordering_fields = ['added_at', 'nickname']
    filterset_fields = ['gear__gear_type']

    def get_queryset(self):
        return (
            UserGear.objects
            .filter(user=self.request.user)
            .select_related('gear')
        )

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


# ── Rigs ──────────────────────────────────────────────────────────────────────

class RigViewSet(viewsets.ModelViewSet):
    """
    The authenticated user's loadout rigs.

    Items are managed via nested endpoints:
      POST   /rigs/{id}/items/          → add a UserGear to the rig
      DELETE /rigs/{id}/items/{item_id}/ → remove an item from the rig
    """
    serializer_class = RigSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = None
    search_fields = ['name', 'description']
    ordering_fields = ['name', 'created_at']

    def get_queryset(self):
        return (
            Rig.objects
            .filter(user=self.request.user)
            .prefetch_related('rig_items__user_gear__gear__firearm')
        )

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

    @action(detail=True, methods=['post'], url_path='items')
    def add_item(self, request, pk=None):
        rig = self.get_object()
        serializer = RigItemCreateSerializer(
            data=request.data,
            context={'request': request, 'rig': rig},
        )
        serializer.is_valid(raise_exception=True)
        item = serializer.save()
        return Response(
            RigItemReadSerializer(item, context={'request': request}).data,
            status=status.HTTP_201_CREATED,
        )

    @action(
        detail=True,
        methods=['delete'],
        url_path=r'items/(?P<item_pk>[^/.]+)',
    )
    def remove_item(self, request, pk=None, item_pk=None):
        rig = self.get_object()
        item = get_object_or_404(RigItem, pk=item_pk, rig=rig)
        item.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


# ── Ammo catalog ──────────────────────────────────────────────────────────────

class AmmoViewSet(GearCatalogMixin, viewsets.ModelViewSet):
    """
    Commercial/factory ammunition catalog.
    Same moderation flow as gear types: users submit PENDING, staff verify/reject.
    """
    queryset = Ammo.objects.select_related('submitted_by', 'reviewed_by', 'caliber')
    serializer_class = AmmoSerializer
    search_fields = ['brand', 'name', 'caliber__name']
    ordering_fields = ['brand', 'name', 'caliber__name', 'bullet_weight_gr', 'created_at']
    filterset_fields = ['bullet_type', 'caliber', 'primer_size', 'case_material', 'status']


# ── Reloading components ──────────────────────────────────────────────────────

class ComponentViewSetMixin:
    """
    Any authenticated user may submit a component (status=PENDING).
    Staff submissions are auto-verified.  Only staff may update/delete or verify/reject.
    """
    pagination_class = None

    def get_queryset(self):
        from django.db.models import Q
        qs = super().get_queryset()
        if self.request.user.is_staff:
            return qs
        return qs.filter(
            Q(status=GearStatus.VERIFIED) |
            Q(status=GearStatus.PENDING, submitted_by=self.request.user)
        )

    def get_permissions(self):
        if self.action in ('update', 'partial_update', 'destroy'):
            return [IsAdminUser()]
        return [IsAuthenticated()]

    def perform_create(self, serializer):
        if self.request.user.is_staff:
            serializer.save(status=GearStatus.VERIFIED, submitted_by=self.request.user)
        else:
            serializer.save(status=GearStatus.PENDING, submitted_by=self.request.user)

    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def verify(self, request, pk=None):
        obj = self.get_object()
        obj.verify()
        return Response(self.get_serializer(obj).data)

    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def reject(self, request, pk=None):
        obj = self.get_object()
        obj.reject()
        return Response(self.get_serializer(obj).data)


class PrimerViewSet(ComponentViewSetMixin, viewsets.ModelViewSet):
    queryset = Primer.objects.all()
    serializer_class = PrimerSerializer
    search_fields = ['brand', 'name']
    ordering_fields = ['brand', 'name']
    filterset_fields = ['size']


class BrassViewSet(ComponentViewSetMixin, viewsets.ModelViewSet):
    queryset = Brass.objects.select_related('caliber')
    serializer_class = BrassSerializer
    search_fields = ['brand', 'caliber__name']
    ordering_fields = ['brand', 'caliber__name']
    filterset_fields = ['caliber', 'primer_pocket']


class BulletViewSet(ComponentViewSetMixin, viewsets.ModelViewSet):
    queryset = Bullet.objects.all()
    serializer_class = BulletSerializer
    search_fields = ['brand', 'model_name']
    ordering_fields = ['brand', 'model_name', 'weight_gr']
    filterset_fields = ['bullet_type']


class PowderViewSet(ComponentViewSetMixin, viewsets.ModelViewSet):
    queryset = Powder.objects.all()
    serializer_class = PowderSerializer
    search_fields = ['brand', 'name']
    ordering_fields = ['brand', 'name', 'burn_rate_index']
    filterset_fields = ['powder_type']


# ── Reload development ────────────────────────────────────────────────────────

class ReloadRecipeViewSet(viewsets.ModelViewSet):
    """
    User's reload recipes (fixed primer + brass + bullet combinations).
    Batches (different powder charges) are created via /reloading/batches/.
    """
    serializer_class = ReloadRecipeSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = None
    search_fields = ['name', 'caliber__name']
    ordering_fields = ['name', 'caliber__name', 'created_at']
    filterset_fields = ['caliber']

    def get_queryset(self):
        return (
            ReloadRecipe.objects
            .filter(user=self.request.user)
            .select_related('primer', 'brass', 'bullet')
            .prefetch_related('batches__powder')
        )

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

    @action(detail=True, methods=['get'], url_path='stats')
    def stats(self, request, pk=None):
        """
        Per-batch velocity statistics for all batches in this recipe.
        Useful for comparing powder charges and identifying the optimal load.
        """
        # Local imports to avoid circular module-level dependency tools ↔ gears
        from apps.tools.models import Shot
        from apps.tools.serializers import _compute_stats

        recipe = self.get_object()
        result = []
        for batch in recipe.batches.select_related('powder').prefetch_related(
            'shot_groups__shots'
        ):
            all_shots = Shot.objects.filter(group__ammo_batch=batch)
            result.append({
                'batch_id':         batch.pk,
                'powder':           str(batch.powder),
                'powder_charge_gr': str(batch.powder_charge_gr),
                'stats':            _compute_stats(all_shots),
            })
        return Response(result)


class ReloadedAmmoBatchViewSet(viewsets.ModelViewSet):
    """
    Individual powder charge batches under a recipe.
    Filter by recipe using ?recipe=<id>.
    """
    serializer_class = ReloadedAmmoBatchSerializer
    permission_classes = [IsAuthenticated]
    pagination_class = None
    search_fields = ['notes', 'powder__name', 'powder__brand']
    ordering_fields = ['powder_charge_gr', 'loaded_at', 'created_at']
    filterset_fields = ['recipe', 'powder']

    def get_queryset(self):
        return (
            ReloadedAmmoBatch.objects
            .filter(recipe__user=self.request.user)
            .select_related('recipe', 'powder')
        )

    @action(detail=True, methods=['get'], url_path='stats')
    def stats(self, request, pk=None):
        """Velocity statistics for all ShotGroups linked to this batch."""
        # Local imports to avoid circular module-level dependency tools ↔ gears
        from apps.tools.models import Shot
        from apps.tools.serializers import _compute_stats

        batch = self.get_object()
        all_shots = Shot.objects.filter(group__ammo_batch=batch)
        return Response(_compute_stats(all_shots))
