from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.db.models import Count, Q
from apps.jobs.models import JobDescription
from apps.candidates.models import Candidate
from apps.clients.models import Client
from apps.ai_calls.models import AICall
from apps.pipeline.models import JobApplication, PipelineStage
from apps.audit_logs.models import AuditLog
from core.responses import success_response, error_response

User = get_user_model()


class AdminCommandCenterView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        # Optional client scope: ?client=<id> narrows every JD-rooted metric to
        # that client. Users/candidates/AI-calls/offers have no direct client FK,
        # so they are scoped through the client's JobDescriptions.
        client = None
        client_param = request.query_params.get("client")
        if client_param:
            try:
                client = Client.objects.get(pk=int(client_param))
            except (Client.DoesNotExist, ValueError, TypeError):
                return error_response("Client not found", status_code=404)

        # 1. Overview Cards (KPIs)
        if client:
            client_jds = JobDescription.objects.filter(client=client)
            # Users tied to this client = JD creators + assigned recruiters
            creator_ids = client_jds.exclude(created_by=None).values_list("created_by_id", flat=True)
            recruiter_ids = User.objects.filter(
                jd_assignments__jd__client=client
            ).values_list("id", flat=True)
            total_users = len(set(creator_ids) | set(recruiter_ids))
            total_recruiters = User.objects.filter(
                role="RECRUITER", jd_assignments__jd__client=client
            ).distinct().count()
            open_jds = client_jds.filter(status="Published").count()
            total_candidates = (
                JobApplication.objects.filter(job__client=client)
                .values_list("candidate_id", flat=True).distinct().count()
            )
            total_ai_calls = AICall.objects.filter(job__client=client).count()
        else:
            total_users = User.objects.count()
            total_recruiters = User.objects.filter(role="RECRUITER").count()
            open_jds = JobDescription.objects.filter(status="Published").count()
            total_candidates = Candidate.objects.count()
            total_ai_calls = AICall.objects.count()

        # Get offer stage
        offer_stage = PipelineStage.objects.filter(code__in=["offered", "placed"]).values_list("id", flat=True)
        offer_qs = JobApplication.objects.filter(stage_id__in=offer_stage)
        if client:
            offer_qs = offer_qs.filter(job__client=client)
        total_offers = offer_qs.count()

        # 2. Today's Activity Table (global only — AuditLog has no client link,
        # and the client view hides this section)
        user_activities = {}
        if not client:
            today = timezone.localtime().date()
            today_logs = AuditLog.objects.filter(created_at__date=today).select_related("user")

            for log in today_logs:
                u_email = log.user.email if log.user else "system"
                u_name = log.user.full_name if (log.user and log.user.full_name) else (log.user.email.split("@")[0].capitalize() if log.user else "System")

                if u_email not in user_activities:
                    user_activities[u_email] = {
                        "email": u_email,
                        "name": u_name,
                        "role": log.user.role if log.user else "SYSTEM",
                        "actions_count": 0,
                        "last_action": log.action,
                        "last_action_time": log.created_at.isoformat()
                    }
                user_activities[u_email]["actions_count"] += 1

        # 3. Open JDs Table with Progress
        published_jds = JobDescription.objects.filter(status="Published").prefetch_related("assigned_recruiters", "applications", "applications__stage")
        if client:
            published_jds = published_jds.filter(client=client)
        jds_data = []
        for jd in published_jds:
            recruiters = [r.full_name or r.email for r in jd.assigned_recruiters.all()]
            
            # Application stages count
            total_apps = jd.applications.count()
            in_progress = jd.applications.filter(stage__outcome="IN_PROGRESS").count()
            placed = jd.applications.filter(stage__outcome="WON").count()
            rejected = jd.applications.filter(stage__outcome="LOST").count()

            jds_data.append({
                "id": jd.id,
                "title": jd.title,
                "client": jd.client.name if jd.client else "Internal",
                "priority": jd.priority,
                "assigned_recruiters": recruiters,
                "progress": {
                    "total": total_apps,
                    "in_progress": in_progress,
                    "placed": placed,
                    "rejected": rejected
                },
                "created_at": jd.created_at.isoformat()
            })

        # 4. Recruiter Performance (global only — hidden in the client view)
        recruiters_list = [] if client else User.objects.filter(role="RECRUITER").prefetch_related("candidates_created", "jd_assignments")
        rec_data = []
        for r in recruiters_list:
            cand_count = r.candidates_created.count()
            calls_completed = AICall.objects.filter(created_by=r, status=AICall.Status.COMPLETED).count()
            
            # Active assigned jobs
            assigned_active_jds = r.jd_assignments.filter(jd__status="Published").count()
            
            rec_data.append({
                "id": r.id,
                "name": r.full_name or r.email.split("@")[0].capitalize(),
                "email": r.email,
                "candidates_added": cand_count,
                "calls_completed": calls_completed,
                "assigned_active_jds": assigned_active_jds
            })

        # 5. Recent Activity Feed (global only — hidden in the client view)
        recent_logs = [] if client else AuditLog.objects.select_related("user").all()[:30]
        logs_data = []
        for log in recent_logs:
            user_name = "System"
            user_email = "system"
            if log.user:
                user_email = log.user.email
                user_name = log.user.full_name if log.user.full_name else log.user.email.split("@")[0].capitalize()
                
            logs_data.append({
                "id": log.id,
                "user_email": user_email,
                "user_name": user_name,
                "action": log.action,
                "description": log.description,
                "ip_address": log.ip_address or "127.0.0.1",
                "created_at": log.created_at.isoformat()
            })

        return success_response({
            "client": {"id": client.id, "name": client.name} if client else None,
            "kpis": {
                "total_users": total_users,
                "total_recruiters": total_recruiters,
                "open_jds": open_jds,
                "total_candidates": total_candidates,
                "total_ai_calls": total_ai_calls,
                "total_offers": total_offers
            },
            "today_activities": list(user_activities.values()),
            "open_jds_progress": jds_data,
            "recruiter_performance": rec_data,
            "recent_activity": logs_data
        })


from rest_framework.response import Response

class RecruiterDashboardView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user

        # Optional `?jd=<id>` narrows every metric below to one Job Description.
        # The JD set always starts from this recruiter's own assignments, so an
        # unassigned/unknown id simply yields an empty scope — a recruiter can
        # never read another JD's numbers by passing its id. Omitting the param
        # keeps the previous behaviour (all assigned JDs) exactly.
        my_jds = JobDescription.objects.filter(assigned_recruiters=user)
        from_date = request.query_params.get("from_date")
        to_date = request.query_params.get("to_date")
        if from_date:
            my_jds = my_jds.filter(created_at__date__gte=from_date)
        if to_date:
            my_jds = my_jds.filter(created_at__date__lte=to_date)
        # Optional `?client=` / `?status=` narrow the same scope. Both are
        # additive: omitting them keeps the previous behaviour exactly, and
        # neither can widen the scope beyond the recruiter's own assignments.
        client_param = (request.query_params.get("client") or "").strip()
        if client_param:
            my_jds = my_jds.filter(client_id=client_param) if client_param.isdigit() else my_jds.none()
        status_param = (request.query_params.get("status") or "").strip()
        if status_param and status_param.lower() != "all":
            my_jds = my_jds.filter(status=status_param)

        jd_param = (request.query_params.get("jd") or "").strip()
        if jd_param and jd_param.lower() != "all":
            my_jds = my_jds.filter(id=jd_param) if jd_param.isdigit() else my_jds.none()
        my_jd_ids = list(my_jds.values_list("id", flat=True).distinct())

        # Count active JDs assigned to recruiter
        assigned_jds = my_jds.filter(status="Published").distinct().count()

        recruiter_apps = JobApplication.objects.filter(job_id__in=my_jd_ids)
        
        in_process = recruiter_apps.filter(
            stage__code__in=[
                "contacted",
                "candidate_responded",
                "ai_calling",
                "ai_screened",
                "ai_qualified",
                "qualifying",
                "interviewing",
                "selected",
            ]
        ).count()
        
        ai_screening = recruiter_apps.filter(
            stage__code__in=["ai_calling", "ai_screened"]
        ).count()
        
        submitted = recruiter_apps.filter(stage__code="submitted").count()
        
        shortlisted = recruiter_apps.filter(stage__code="selected").count()
        
        offered = recruiter_apps.filter(stage__code="offered").count()
        
        joined = recruiter_apps.filter(stage__code="placed").count()
        
        offer_rejected = recruiter_apps.filter(
            stage__code__in=["offer_declined", "offered_not_joined"]
        ).count()
        
        rejected = recruiter_apps.filter(
            stage__code__in=["not_in_consideration", "client_declined"]
        ).count()
        
        # Ratio calculations need cumulative historical counts to be accurate:
        submitted_cum = recruiter_apps.filter(
            stage__code__in=[
                "submitted",
                "interviewing",
                "selected",
                "offered",
                "placed",
                "offer_declined",
                "offered_not_joined",
            ]
        ).count()
        
        shortlisted_cum = recruiter_apps.filter(
            stage__code__in=[
                "selected",
                "offered",
                "placed",
                "offer_declined",
                "offered_not_joined",
            ]
        ).count()
        
        offered_cum = recruiter_apps.filter(
            stage__code__in=["offered", "placed", "offer_declined", "offered_not_joined"]
        ).count()
        
        joined_cum = joined
        
        offer_rejected_cum = offer_rejected
 
        shortlisting_offer_ratio = (
            round((offered_cum / shortlisted_cum) * 100, 1) if shortlisted_cum > 0 else 0.0
        )
        offer_join_ratio = round((joined_cum / offered_cum) * 100, 1) if offered_cum > 0 else 0.0
        offered_drop_ratio = (
            round((offer_rejected_cum / offered_cum) * 100, 1) if offered_cum > 0 else 0.0
        )
        shortlisted_ratio = (
            round((shortlisted_cum / submitted_cum) * 100, 1) if submitted_cum > 0 else 0.0
        )
        
        # Turnaround Time (TAT) in Days
        closed_apps = recruiter_apps.filter(
            stage__outcome__in=[PipelineStage.Outcome.WON, PipelineStage.Outcome.LOST]
        )
        tats = []
        for app in closed_apps:
            delta = app.updated_at - app.created_at
            tats.append(max(0, delta.days))
            
        avg_tat_days = round(sum(tats) / len(tats), 1) if tats else 0.0
        fastest_tat = min(tats) if tats else 0
        slowest_tat = max(tats) if tats else 0
        
        data = {
            "assigned_jds": assigned_jds,
            "in_process": in_process,
            "ai_screening": ai_screening,
            "submitted": submitted,
            "shortlisted": shortlisted,
            "offered": offered,
            "joined": joined,
            "offer_rejected": offer_rejected,
            "rejected": rejected,
            "shortlisting_offer_ratio": shortlisting_offer_ratio,
            "offer_join_ratio": offer_join_ratio,
            "offered_drop_ratio": offered_drop_ratio,
            "shortlisted_ratio": shortlisted_ratio,
            "avg_tat_days": avg_tat_days,
            "fastest_tat": fastest_tat,
            "slowest_tat": slowest_tat,
        }
        return Response(data)


class HiringManagerDashboardView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        user = request.user
        
        user_role = (user.role or "").upper()
        if user_role == "ADMIN":
            pending_approvals = JobDescription.objects.filter(approval_status="PENDING_APPROVAL").count()
            approved_jds = JobDescription.objects.filter(approval_status="APPROVED").count()
            rejected_jds = JobDescription.objects.filter(approval_status="REJECTED").count()
        else:
            pending_approvals = JobDescription.objects.filter(approval_status="PENDING_APPROVAL", current_approver=user).count()
            approved_jds = JobDescription.objects.filter(approved_by=user).count()
            rejected_jds = JobDescription.objects.filter(
                approval_status="REJECTED",
                approval_histories__action="REJECTED",
                approval_histories__action_by=user
            ).distinct().count()

        data = {
            "pending_approvals": pending_approvals,
            "approved_jds": approved_jds,
            "rejected_jds": rejected_jds
        }
        return Response(data)


