from django.shortcuts import get_object_or_404
from decimal import Decimal
from rest_framework import serializers
from .constants import SOURCE_NPC, SPECIALIST_ROLES, STAFF_TIERS
from django.db.models import Exists, OuterRef
from .employment import StaffEmploymentService
from club_payroll.exceptions import PayrollError
from economy.exceptions import EconomyError
from .exceptions import StaffIntegrityError
from rest_framework import permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from api_core.application import mutation
from api_core.pagination import limited
from api_core.permissions import require_club_manager
from clubs.models import Club
from .api_serializers import CapabilityInputSerializer,SpecialistCreateSerializer,StaffAssignSerializer,StaffAssignmentSerializer,StaffCapabilitySerializer,StaffMemberSerializer
from .exceptions import StaffDomainError
from .models import ClubStaffAssignment,StaffMember
from .services import ClubStaffAssignmentService,StaffCapabilityService,StaffRegistrationService

class ClubStaffAPIView(APIView):
    permission_classes=[permissions.IsAuthenticated]
    def _club(self,request,club_id):
        c=get_object_or_404(Club.objects.select_related("coach_career__user","ownership_record__owner"),pk=club_id);require_club_manager(request.user,c);return c
    def get(self,request,club_id):
        self._club(request,club_id);qs=ClubStaffAssignment.objects.filter(club_id=club_id).select_related("staff_member__capability").order_by("role","-started_at");return limited(request,qs,StaffAssignmentSerializer)
class HeadCoachRegisterAPIView(APIView):
    permission_classes=[permissions.IsAdminUser]
    def post(self,request,club_id):
        # No self-assigned perfect head coach attributes by ordinary club managers.
        c=get_object_or_404(Club,pk=club_id)
        s=CapabilityInputSerializer(data=request.data);s.is_valid(raise_exception=True)
        return mutation(request=request,scope=f"staff.club.{club_id}.head-coach",operation=lambda:StaffRegistrationService.register_head_coach(club_id=club_id,**s.validated_data),serialize=lambda x:{"member":StaffMemberSerializer(x[0]).data,"assignment":StaffAssignmentSerializer(x[1]).data},success_status=201,domain_errors=(StaffDomainError,))

class SpecialistCreateAPIView(APIView):
    permission_classes=[permissions.IsAuthenticated]
    def post(self,request,club_id):
        self_club=get_object_or_404(Club.objects.select_related('coach_career__user','ownership_record__owner'),pk=club_id)
        require_club_manager(request.user,self_club)
        return Response({"detail":"Managers cannot generate NPC staff or set ratings. Hire a registered free agent with a salary contract."},status=403)

class AvailableStaffAPIView(APIView):
    permission_classes=[permissions.IsAuthenticated]
    http_method_names=['get','head','options']
    def get(self,request):
        active=ClubStaffAssignment.objects.filter(staff_member_id=OuterRef('pk'),status='ACTIVE')
        qs=StaffMember.objects.filter(source_type=SOURCE_NPC,specialty__in=SPECIALIST_ROLES,capability__isnull=False)
        specialty=request.query_params.get("specialty")
        tier=request.query_params.get("tier")
        if specialty:
            if specialty not in SPECIALIST_ROLES: raise serializers.ValidationError("Unknown specialist role.")
            qs=qs.filter(specialty=specialty)
        if tier:
            if tier not in STAFF_TIERS: raise serializers.ValidationError("Unknown specialist tier.")
            qs=qs.filter(tier=tier)
        qs=qs.annotate(is_hired=Exists(active)).filter(is_hired=False).select_related('capability').order_by('specialty','minimum_salary','last_name','pk')[:100]
        return Response(StaffMemberSerializer(qs,many=True).data)

class StaffHirePayload(serializers.Serializer):
    staff_member_id=serializers.UUIDField()
    monthly_salary=serializers.DecimalField(max_digits=20,decimal_places=8,min_value=Decimal('0.00000001'))
    duration_days=serializers.IntegerField(min_value=30,max_value=730)
    def to_internal_value(self,data):
        if hasattr(data,'keys'):
            extra=set(data.keys())-set(self.fields.keys())
            if extra:raise serializers.ValidationError({name:['Unexpected field'] for name in extra})
        return super().to_internal_value(data)

class StaffHireAPIView(APIView):
    permission_classes=[permissions.IsAuthenticated]
    def post(self,request,club_id):
        club=get_object_or_404(Club.objects.select_related('coach_career__user','ownership_record__owner'),pk=club_id)
        require_club_manager(request.user,club)
        serializer=StaffHirePayload(data=request.data);serializer.is_valid(raise_exception=True)
        return mutation(request=request,scope=f'staff.club.{club_id}.hire',
            operation=lambda:StaffEmploymentService.hire(club=club,**serializer.validated_data),
            serialize=lambda pair:{'assignment':StaffAssignmentSerializer(pair[0]).data,'payroll_plan_id':str(pair[1].pk),'monthly_salary':str(pair[1].installment_amount),'contract_end':pair[1].end_date.isoformat()},
            success_status=201,domain_errors=(StaffDomainError,PayrollError,EconomyError,))

class StaffCapabilityUpdateAPIView(APIView):
    permission_classes=[permissions.IsAdminUser]
    def post(self,request,staff_member_id):
        m=get_object_or_404(StaffMember.objects.prefetch_related("club_assignments__club__ownership_record","club_assignments__club__coach_career__user"),pk=staff_member_id);active=m.club_assignments.filter(status="ACTIVE").select_related("club__coach_career__user","club__ownership_record__owner").first()
        if not active:return Response({"detail":"Only assigned staff can be modified through club API."},status=403)
        s=CapabilityInputSerializer(data=request.data);s.is_valid(raise_exception=True)
        return mutation(request=request,scope=f"staff.member.{staff_member_id}.capability",operation=lambda:StaffCapabilityService.update(staff_member_id=staff_member_id,**s.validated_data),serialize=lambda x:StaffCapabilitySerializer(x).data,domain_errors=(StaffDomainError,))
class StaffAssignmentEndAPIView(APIView):
    permission_classes=[permissions.IsAuthenticated]
    def post(self,request,assignment_id):
        a=get_object_or_404(ClubStaffAssignment.objects.select_related("club__coach_career__user","club__ownership_record__owner"),pk=assignment_id);require_club_manager(request.user,a.club)
        return mutation(request=request,scope=f"staff.assignment.{assignment_id}.end",operation=lambda:_end_assignment(request.user,a),serialize=lambda x:StaffAssignmentSerializer(x).data,domain_errors=(StaffDomainError,))


def _end_assignment(user, assignment):
    if assignment.role == 'HEAD_COACH':
        from clubs.coach_employment import end
        end(user=user,club_id=assignment.club_id,reason='DISMISSED')
        assignment.refresh_from_db()
        return assignment
    return StaffEmploymentService.end(assignment_id=assignment.pk)
