from rest_framework import viewsets, status, filters
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 apps.core.pagination import StandardResultsSetPagination
from apps.core.tenancy import TenantScopedMixin

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


class AssetViewSet(TenantScopedMixin, 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
    pagination_class = StandardResultsSetPagination
    filter_backends = [filters.SearchFilter]
    search_fields = ['asset_tag', 'serial_number', 'name']

    def get_queryset(self):
        qs = super().get_queryset()
        for param, field in (("status", "status"), ("category", "category_id"), ("location", "location_id"), ("goods_receipt", "goods_receipt_id")):
            val = self.request.query_params.get(param)
            if val:
                qs = qs.filter(**{field: val})
        return qs

    @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"),
            OpenApiParameter(name="goods_receipt", type=str, description="Filter by goods receipt ID"),
        ]
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AssetCategoryViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for AssetCategory management."""
    rbac_domain = "asset_management"
    queryset = AssetCategory.objects.all()
    serializer_class = AssetCategorySerializer
    pagination_class = None  # Small fixed catalog, never exceeds ~50 rows.

    def get_queryset(self):
        return super().get_queryset()


class AssetLocationViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for AssetLocation management."""
    rbac_domain = "asset_management"
    queryset = AssetLocation.objects.all()
    serializer_class = AssetLocationSerializer
    pagination_class = None  # Small fixed catalog, never exceeds ~50 rows.

    def get_queryset(self):
        return super().get_queryset()


def _notify_asset(user, *, title, message, link="", priority="medium", related_user=None):
    """Fire an in-app asset notification; never break the request on failure."""
    from apps.notifications.utils import notify
    notify(
        user, title=title, message=message, link=link, priority=priority,
        related_user=related_user, notification_type="asset", icon="box",
    )


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

    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="/asset-management/assignment",
                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(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Maintenance management."""
    rbac_domain = "asset_management"
    organization_lookup = "asset__organization"
    queryset = Maintenance.objects.select_related("asset")
    serializer_class = MaintenanceSerializer
    pagination_class = StandardResultsSetPagination

    def perform_create(self, serializer):
        serializer.save()

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

    @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(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Procurement management."""
    rbac_domain = "asset_management"
    queryset = Procurement.objects.select_related("requested_by", "vendor", "asset")
    serializer_class = ProcurementSerializer
    pagination_class = StandardResultsSetPagination

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

    def perform_create(self, serializer):
        from apps.core.tenancy import get_active_organization
        serializer.save(requested_by=self.request.user, organization=get_active_organization(self.request))

    @extend_schema(
        parameters=[
            OpenApiParameter(name="status", type=str, description="Filter by status"),
            OpenApiParameter(name="requested_by", type=str, description="Filter by requester ID"),
            OpenApiParameter(name="asset", type=str, description="Filter by linked asset 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(
            organization=proc.organization,
            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"])

        _notify_asset(
            proc.requested_by,
            title="Procurement promoted",
            message=f"“{proc.title}” has been promoted to requisition and is now in the full procurement chain.",
            link=f"/procurements/requisitions/{req.id}",
        )
        return Response(
            RequisitionSerializer(req).data, status=status.HTTP_201_CREATED
        )


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

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

    def perform_create(self, serializer):
        from apps.core.tenancy import get_active_organization
        serializer.save(reported_by=self.request.user, organization=get_active_organization(self.request))

    @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(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Vendor management."""
    rbac_domain = "asset_management"
    queryset = Vendor.objects.all()
    serializer_class = VendorSerializer

    def get_queryset(self):
        return super().get_queryset()

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

    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(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for License / subscription management."""
    rbac_domain = "asset_management"
    queryset = License.objects.select_related("vendor", "procurement").prefetch_related("seat_assignments")
    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 LicenseSeatAssignmentViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for LicenseSeatAssignment management."""
    rbac_domain = "asset_management"
    organization_lookup = "license__organization"
    queryset = LicenseSeatAssignment.objects.select_related("license", "employee__user")
    serializer_class = LicenseSeatAssignmentSerializer
    pagination_class = StandardResultsSetPagination

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

    def perform_create(self, serializer):
        assignment = serializer.save()
        target = getattr(assignment.employee, "user", None)
        _notify_asset(
            target,
            title="License seat assigned",
            message=f"You have been assigned a seat for {assignment.license.name}.",
            link=f"/asset-management/licenses/{assignment.license_id}",
        )

    def perform_update(self, serializer):
        was_assigned = serializer.instance.status == "assigned"
        assignment = serializer.save()
        if was_assigned and assignment.status == "returned":
            target = getattr(assignment.employee, "user", None)
            _notify_asset(
                target,
                title="License seat returned",
                message=f"Your seat for {assignment.license.name} has been returned.",
                link=f"/asset-management/licenses/{assignment.license_id}",
            )

    @extend_schema(parameters=[
        OpenApiParameter(name="license", type=str, description="Filter by license 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 RentalViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Rental management."""
    rbac_domain = "asset_management"
    queryset = Rental.objects.select_related("vendor", "location", "procurement")
    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(TenantScopedMixin, 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(TenantScopedMixin, viewsets.ReadOnlyModelViewSet):
    """Read-only per-location stock levels. Quantities change only via movements."""
    rbac_domain = "asset_management"
    organization_lookup = "item__organization"
    queryset = StockLevel.objects.select_related("item", "location")
    serializer_class = StockLevelSerializer
    pagination_class = StandardResultsSetPagination

    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 SellableItemViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for the sellable catalog (books and accessories)."""
    rbac_domain = "asset_management"
    organization_lookup = "stock_item__organization"
    queryset = SellableItem.objects.select_related("stock_item", "publication").prefetch_related("stock_item__levels")
    serializer_class = SellableItemSerializer

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

    def perform_create(self, serializer):
        serializer.save()

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


class StockMovementViewSet(TenantScopedMixin, 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"
    organization_lookup = "item__organization"
    http_method_names = ["get", "post", "head", "options"]
    queryset = StockMovement.objects.select_related("item", "location", "performed_by")
    serializer_class = StockMovementSerializer
    pagination_class = StandardResultsSetPagination

    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)
