from rest_framework import viewsets, status
from rest_framework.authentication import SessionAuthentication
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter

from apps.core.pagination import StandardResultsSetPagination
from apps.core.tenancy import TenantScopedMixin, get_active_organization

from .models import Company, Organization, Department, Director
from .serializers import CompanySerializer, OrgProfileSerializer, DepartmentSerializer, DirectorSerializer


class CompanyViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for Company management."""
    rbac_domain = "phonebook"
    queryset = Company.objects.select_related("created_by")
    serializer_class = CompanySerializer
    parser_classes = [JSONParser, MultiPartParser, FormParser]
    pagination_class = StandardResultsSetPagination

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

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

    @action(detail=True, methods=["post", "delete"], url_path="logo",
            parser_classes=[MultiPartParser, FormParser, JSONParser])
    def logo(self, request, pk=None):
        company = self.get_object()
        if request.method == "DELETE":
            if company.logo:
                company.logo.delete(save=False)
            company.logo = None
            company.save(update_fields=["logo", "updated_at"])
            return Response(self.get_serializer(company).data)

        file = request.FILES.get("logo")
        if not file:
            return Response({"detail": "Missing 'logo' file."},
                            status=status.HTTP_400_BAD_REQUEST)
        from apps.core.uploads import validate_upload
        validate_upload(file)
        if company.logo:
            company.logo.delete(save=False)
        company.logo = file
        company.save(update_fields=["logo", "updated_at"])
        return Response(self.get_serializer(company).data)


class OrgProfileViewSet(viewsets.ViewSet):
    """Singleton org profile. GET /org-profile/ returns the row, PATCH updates it."""
    rbac_domain = "phonebook"
    parser_classes = [JSONParser, MultiPartParser, FormParser]

    def _get_singleton(self, request):
        return get_active_organization(request)

    def _may_update_profile(self, request):
        """Same decision HasMenuPermission would make for an update action on
        this viewset's rbac_domain, re-applied by hand because `current`
        overrides permission_classes with AllowAny for the public GET."""
        user = request.user
        if user.is_superuser or getattr(user, "organization_role", "staff") == "director":
            return True
        return user.has_menu_perm(f"{self.rbac_domain}.update", get_active_organization(request))

    def list(self, request):
        obj = self._get_singleton(request)
        return Response(OrgProfileSerializer(obj, context={"request": request}).data)

    @action(detail=False, methods=["get", "patch", "put"], url_path="current",
            permission_classes=[AllowAny], authentication_classes=[SessionAuthentication])
    def current(self, request):
        # GET is public so the app shell can render branding (favicon/colors)
        # before login. An anonymous caller has no organization to resolve, so
        # it gets an empty response rather than another tenant's private
        # branding — the frontend already treats null/empty branding as
        # "use CSS defaults". Writes still require real authentication.
        if request.method in ("PATCH", "PUT"):
            if not request.user.is_authenticated:
                return Response({"detail": "Authentication required."}, status=status.HTTP_403_FORBIDDEN)
            # AllowAny above disables the viewset's rbac_domain gate for this
            # action, so the write path has to re-assert it explicitly.
            # Otherwise any authenticated member could rewrite the org's legal,
            # contact, tax and branding fields (SEC-04).
            if not self._may_update_profile(request):
                return Response({"detail": "You do not have permission to edit the organization profile."},
                                status=status.HTTP_403_FORBIDDEN)
        if not request.user.is_authenticated:
            return Response({})
        try:
            obj = self._get_singleton(request)
        except PermissionDenied:
            return Response({})
        if request.method in ("PATCH", "PUT"):
            partial = request.method == "PATCH"
            serializer = OrgProfileSerializer(obj, data=request.data, partial=partial, context={"request": request})
            serializer.is_valid(raise_exception=True)
            serializer.save()
            return Response(serializer.data)
        return Response(OrgProfileSerializer(obj, context={"request": request}).data)


class DepartmentViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for internal Department records."""
    rbac_domain = "phonebook"
    queryset = Department.objects.all().select_related("parent", "head__user")
    serializer_class = DepartmentSerializer

    def get_serializer_context(self):
        ctx = super().get_serializer_context()
        # Employee GROUP BY runs for every action otherwise (incl. writes);
        # 60s staleness on a headcount badge is fine.
        from django.core.cache import cache
        org = get_active_organization(self.request)
        cache_key = f"dept_counts:{org.id}"
        counts = cache.get(cache_key)
        if counts is None:
            from apps.hr.models import Employee
            from django.db.models import Count
            counts = dict(
                Employee.objects.filter(organization=org).values_list("department").annotate(c=Count("id"))
            )
            cache.set(cache_key, counts, 60)
        ctx["dept_counts"] = counts
        return ctx


class DirectorViewSet(TenantScopedMixin, viewsets.ModelViewSet):
    """ViewSet for board / executive Director records."""
    rbac_domain = "phonebook"
    queryset = Director.objects.all().select_related("employee__user__contact_profile")
    serializer_class = DirectorSerializer
    parser_classes = [JSONParser, MultiPartParser, FormParser]
