from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter
from django.db.models import Prefetch

from .models import (
    Asset, AssetCategory, AssetLocation, AssetAssignment, AssetTicket, AssetDocument,
    Maintenance, Procurement, Vendor, License, Rental, StockItem, StockLevel, StockMovement,
)
from .serializers import (
    AssetSerializer, AssetCategorySerializer, AssetLocationSerializer,
    AssetAssignmentSerializer, AssetTicketSerializer, AssetDocumentSerializer,
    MaintenanceSerializer, ProcurementSerializer, VendorSerializer,
    LicenseSerializer, RentalSerializer, StockItemSerializer, StockLevelSerializer, StockMovementSerializer,
)


class AssetViewSet(viewsets.ModelViewSet):
    """ViewSet for Asset management."""
    rbac_domain = "asset_management"
    queryset = Asset.objects.all().select_related(
        'category', 'location', 'vendor', 'purchase_order', 'goods_receipt'
    ).prefetch_related(
        Prefetch('assignments', queryset=AssetAssignment.objects.filter(status='assigned').select_related('employee', 'employee__user'))
    )
    serializer_class = AssetSerializer

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="category", type=str, description="Filter by category ID"),
            OpenApiParameter(name="location", type=str, description="Filter by location ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AssetCategoryViewSet(viewsets.ModelViewSet):
    """ViewSet for AssetCategory management."""
    rbac_domain = "asset_management"
    queryset = AssetCategory.objects.all()
    serializer_class = AssetCategorySerializer


class AssetLocationViewSet(viewsets.ModelViewSet):
    """ViewSet for AssetLocation management."""
    rbac_domain = "asset_management"
    queryset = AssetLocation.objects.all()
    serializer_class = AssetLocationSerializer


def _notify_asset(user, *, title, message, link="", priority="medium", related_user=None):
    """Fire an in-app asset notification; never break the request on failure."""
    if not user:
        return
    try:
        from apps.notifications.handler import NotificationHandler
        NotificationHandler.send(
            user=user, title=title, message=message,
            notification_type="asset", priority=priority, icon="box",
            link=link, related_user=related_user, broadcast=True,
        )
    except Exception:
        pass


class AssetAssignmentViewSet(viewsets.ModelViewSet):
    """ViewSet for AssetAssignment management."""
    rbac_domain = "asset_management"
    queryset = AssetAssignment.objects.select_related("asset__category", "employee__user")
    serializer_class = AssetAssignmentSerializer

    def perform_create(self, serializer):
        assignment = serializer.save()
        actor = self.request.user if self.request.user.is_authenticated else None
        target = getattr(assignment.employee, "user", None)
        if target and (not actor or target.id != actor.id):
            _notify_asset(
                target,
                title="Asset assigned",
                message=f"You were assigned “{assignment.asset.name}”.",
                link="/assets",
                related_user=actor,
            )

    def get_queryset(self):
        qs = super().get_queryset()
        employee_id = self.request.query_params.get("employee")
        if employee_id:
            qs = qs.filter(employee_id=employee_id)
        asset_id = self.request.query_params.get("asset")
        if asset_id:
            qs = qs.filter(asset_id=asset_id)
        status = self.request.query_params.get("status")
        if status:
            qs = qs.filter(status=status)
        return qs

    @extend_schema(
        parameters=[
            OpenApiParameter(name="asset", type=str, description="Filter by asset ID"),
            OpenApiParameter(name="employee", type=str, description="Filter by employee ID"),
            OpenApiParameter(name="status", type=str, description="Filter by status"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class MaintenanceViewSet(viewsets.ModelViewSet):
    """ViewSet for Maintenance management."""
    rbac_domain = "asset_management"
    queryset = Maintenance.objects.select_related("asset")
    serializer_class = MaintenanceSerializer

    @extend_schema(
        parameters=[
            OpenApiParameter(name="asset", type=str, description="Filter by asset ID"),
            OpenApiParameter(name="status", type=str, description="Filter by status"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class ProcurementViewSet(viewsets.ModelViewSet):
    """ViewSet for Procurement management."""
    rbac_domain = "asset_management"
    queryset = Procurement.objects.select_related("requested_by", "vendor")
    serializer_class = ProcurementSerializer

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

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requested_by", type=str, description="Filter by requester ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    @action(detail=True, methods=["post"])
    def promote(self, request, pk=None):
        """Promote this lite tracker into the full procurement chain.

        Spawns a Requisition from this Procurement's fields, links the two, and
        flips this record to "ordered". Idempotent: refuses if already promoted.
        """
        from apps.procurements.models import Requisition
        from apps.procurements.serializers import RequisitionSerializer

        proc = self.get_object()
        if proc.requisition_id:
            return Response(
                {"detail": "Already promoted.", "requisition": str(proc.requisition_id)},
                status=status.HTTP_409_CONFLICT,
            )

        req = Requisition.objects.create(
            title=proc.title,
            description=proc.description,
            status="pending",
            requested_by=request.user,
            vendor=proc.vendor,
            items=proc.items or [],
            total_amount=proc.estimated_cost,
            notes=f"Promoted from procurement {proc.number}." + (f" {proc.notes}" if proc.notes else ""),
        )
        proc.requisition = req
        proc.status = "ordered"
        proc.save(update_fields=["requisition", "status", "updated_at"])

        return Response(
            RequisitionSerializer(req).data, status=status.HTTP_201_CREATED
        )


class AssetTicketViewSet(viewsets.ModelViewSet):
    """ViewSet for AssetTicket management."""
    rbac_domain = "asset_management"
    queryset = AssetTicket.objects.all().select_related('asset', 'reported_by', 'assigned_to')
    serializer_class = AssetTicketSerializer

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="priority", type=str, description="Filter by priority"),
            OpenApiParameter(name="asset", type=str, description="Filter by asset ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class VendorViewSet(viewsets.ModelViewSet):
    """ViewSet for Vendor management."""
    rbac_domain = "asset_management"
    queryset = Vendor.objects.all()
    serializer_class = VendorSerializer

class AssetDocumentViewSet(viewsets.ModelViewSet):
    """ViewSet for AssetDocument management — manuals, warranty cards, invoices."""
    rbac_domain = "asset_management"
    queryset = AssetDocument.objects.select_related("uploaded_by")
    serializer_class = AssetDocumentSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        asset_id = self.request.query_params.get("asset")
        if asset_id:
            qs = qs.filter(asset_id=asset_id)
        return qs

    def perform_create(self, serializer):
        from apps.core.uploads import validate_upload
        upload = self.request.FILES.get("file")
        if upload is not None:
            validate_upload(upload)
        serializer.save(uploaded_by=self.request.user)

    @extend_schema(
        parameters=[
            OpenApiParameter(name="asset", type=str, description="Filter by asset ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class LicenseViewSet(viewsets.ModelViewSet):
    """ViewSet for License / subscription management."""
    rbac_domain = "asset_management"
    queryset = License.objects.select_related("vendor")
    serializer_class = LicenseSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        for param in ("status", "vendor"):
            val = self.request.query_params.get(param)
            if val:
                qs = qs.filter(**{param: val})
        return qs

    @extend_schema(parameters=[
        OpenApiParameter(name="status", type=str, description="Filter by status"),
        OpenApiParameter(name="vendor", type=str, description="Filter by vendor ID"),
    ])
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class RentalViewSet(viewsets.ModelViewSet):
    """ViewSet for Rental management."""
    rbac_domain = "asset_management"
    queryset = Rental.objects.select_related("vendor", "location")
    serializer_class = RentalSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        for param in ("status", "vendor"):
            val = self.request.query_params.get(param)
            if val:
                qs = qs.filter(**{param: val})
        return qs

    @extend_schema(parameters=[
        OpenApiParameter(name="status", type=str, description="Filter by status"),
        OpenApiParameter(name="vendor", type=str, description="Filter by vendor ID"),
    ])
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class StockItemViewSet(viewsets.ModelViewSet):
    """ViewSet for the consumable StockItem catalog."""
    rbac_domain = "asset_management"
    queryset = StockItem.objects.select_related("category", "vendor").prefetch_related("levels")
    serializer_class = StockItemSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        category = self.request.query_params.get("category")
        if category:
            qs = qs.filter(category_id=category)
        return qs

    def filter_queryset(self, queryset):
        # low_stock compares rolled-up qty vs reorder_point — easiest in Python
        # after the prefetch, the catalog is small enough that this is fine.
        queryset = super().filter_queryset(queryset)
        if self.request.query_params.get("low_stock") in ("1", "true", "True"):
            ids = [it.id for it in queryset if it.total_quantity <= it.reorder_point]
            queryset = queryset.filter(id__in=ids)
        return queryset

    @extend_schema(parameters=[
        OpenApiParameter(name="category", type=str, description="Filter by category ID"),
        OpenApiParameter(name="low_stock", type=bool, description="Only items at/below reorder point"),
    ])
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class StockLevelViewSet(viewsets.ReadOnlyModelViewSet):
    """Read-only per-location stock levels. Quantities change only via movements."""
    rbac_domain = "asset_management"
    queryset = StockLevel.objects.select_related("item", "location")
    serializer_class = StockLevelSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        for param in ("item", "location"):
            val = self.request.query_params.get(param)
            if val:
                qs = qs.filter(**{f"{param}_id": val})
        return qs

    @extend_schema(parameters=[
        OpenApiParameter(name="item", type=str, description="Filter by stock item ID"),
        OpenApiParameter(name="location", type=str, description="Filter by location ID"),
    ])
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class StockMovementViewSet(viewsets.ModelViewSet):
    """Stock ledger. Creating a movement applies it to the matching StockLevel.

    Movements are an append-only ledger — only list/retrieve/create are exposed;
    edits/deletes would desync StockLevel, so they're blocked.
    """
    rbac_domain = "asset_management"
    http_method_names = ["get", "post", "head", "options"]
    queryset = StockMovement.objects.select_related("item", "location", "performed_by")
    serializer_class = StockMovementSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        for param in ("item", "location"):
            val = self.request.query_params.get(param)
            if val:
                qs = qs.filter(**{f"{param}_id": val})
        mtype = self.request.query_params.get("movement_type")
        if mtype:
            qs = qs.filter(movement_type=mtype)
        return qs

    def perform_create(self, serializer):
        user = self.request.user if self.request.user.is_authenticated else None
        movement = serializer.save(performed_by=user)
        movement.apply()

    @extend_schema(parameters=[
        OpenApiParameter(name="item", type=str, description="Filter by stock item ID"),
        OpenApiParameter(name="location", type=str, description="Filter by location ID"),
        OpenApiParameter(name="movement_type", type=str, description="Filter by movement type"),
    ])
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
