"""Overall ATS Dashboard — analytics across all ACTIVE JDs (Published/Active).

Endpoints live under /api/v1/reports/overall-dashboard/ and share the filter
params date_from, date_to, client, job, recruiter, department.

Stage semantics are data-driven off the PipelineStage master:
  - "interview" stages  -> active stages whose name contains "interview"
  - "offered"           -> reached a non-LOST stage named like "offer", or sits
                           in a LOST stage named like "offer" (declined offers)
  - "hired"             -> stage outcome WON
Time-to-hire uses created_at -> updated_at of WON applications (no stage
history is stored, so the last update is the hire timestamp).
"""

from django.db.models import Avg, Count, DurationField, ExpressionWrapper, F, Q
from rest_framework.views import APIView

from core.responses import success_response

from apps.jobs.models import JDRecruiterAssignment
from apps.pipeline.models import PipelineStage

from .permissions import CanViewReports
from .services import (
    active_jobs,
    applications_on,
    build_funnel,
    parse_overall_filters,
    pct,
    stage_breakdown,
)

WON = PipelineStage.Outcome.WON
LOST = PipelineStage.Outcome.LOST

INTERVIEW_Q = Q(stage__name__icontains="interview")
HIRED_Q = Q(stage__outcome=WON)


def _scope(request):
    filters = parse_overall_filters(request)
    jobs = active_jobs(filters)
    return filters, jobs, applications_on(jobs, filters)


def _offered_count(applications):
    """Applications that reached the offer stage (currently at/past it, or
    sitting in an offer-related LOST stage like 'Offer Declined')."""
    stages = list(PipelineStage.objects.filter(is_active=True).order_by("sort_order", "id"))
    offer_stage = next(
        (s for s in stages if s.outcome != LOST and "offer" in s.name.lower()), None
    )
    if not offer_stage:
        return 0
    at_or_past = [
        s.id for s in stages
        if s.outcome != LOST and (s.sort_order, s.id) >= (offer_stage.sort_order, offer_stage.id)
    ]
    lost_after_offer = [s.id for s in stages if s.outcome == LOST and "offer" in s.name.lower()]
    return applications.filter(stage_id__in=at_or_past + lost_after_offer).count()


class OverallSummaryView(APIView):
    """GET /api/v1/reports/overall-dashboard/summary/"""

    permission_classes = [CanViewReports]

    def get(self, request):
        filters, jobs, applications = _scope(request)

        total_candidates = applications.values("candidate_id").distinct().count()
        average = applications.filter(score__isnull=False).aggregate(v=Avg("score"))["v"]

        assignments = JDRecruiterAssignment.objects.filter(jd__in=jobs)
        if filters["recruiter"]:
            assignments = assignments.filter(recruiter_id=filters["recruiter"])
        active_recruiters = assignments.values("recruiter_id").distinct().count()

        hired_apps = applications.filter(HIRED_Q)
        total_hires = hired_apps.values("candidate_id").distinct().count()
        offered = _offered_count(applications)

        time_to_hire = hired_apps.aggregate(
            v=Avg(ExpressionWrapper(F("updated_at") - F("created_at"), output_field=DurationField()))
        )["v"]

        return success_response({
            "total_active_jobs": jobs.count(),
            "total_candidates": total_candidates,
            "active_recruiters": active_recruiters,
            "average_score": round(average, 1) if average is not None else 0,
            "total_interviews": applications.filter(INTERVIEW_Q).count(),
            "total_hires": total_hires,
            "offered_candidates": offered,
            "offer_acceptance_rate": pct(hired_apps.count(), offered),
            "average_time_to_hire": round(time_to_hire.total_seconds() / 86400, 1) if time_to_hire else 0,
        })


class OverallCandidatesByStageView(APIView):
    """GET /api/v1/reports/overall-dashboard/candidates-by-stage/"""

    permission_classes = [CanViewReports]

    def get(self, request):
        _, _, applications = _scope(request)
        return success_response(stage_breakdown(applications))


class OverallConversionFunnelView(APIView):
    """GET /api/v1/reports/overall-dashboard/conversion-funnel/"""

    permission_classes = [CanViewReports]

    def get(self, request):
        _, _, applications = _scope(request)
        return success_response({"funnel": build_funnel(applications)})


def recruiter_performance_rows(filters, jobs, applications):
    """One row per recruiter working the active JDs (assigned to one, or having
    added candidates to one), sorted by hires desc."""
    assignments = JDRecruiterAssignment.objects.filter(jd__in=jobs).select_related("recruiter")
    if filters["recruiter"]:
        assignments = assignments.filter(recruiter_id=filters["recruiter"])

    recruiters = {}  # id -> {user, assigned_jds}
    for a in assignments:
        entry = recruiters.setdefault(a.recruiter_id, {"user": a.recruiter, "assigned_jds": set()})
        entry["assigned_jds"].add(a.jd_id)

    stats = {
        row["created_by_id"]: row
        for row in applications.filter(created_by__isnull=False)
        .values("created_by_id")
        .annotate(
            total=Count("id"),
            interviews=Count("id", filter=INTERVIEW_Q),
            hires=Count("id", filter=HIRED_Q),
            avg_score=Avg("score"),
        )
    }
    # Recruiters with pipeline activity but no assignment still get a row
    creators = {app.created_by_id: app.created_by for app in applications if app.created_by_id}
    for uid, user in creators.items():
        recruiters.setdefault(uid, {"user": user, "assigned_jds": set()})

    rows = []
    for uid, entry in recruiters.items():
        user = entry["user"]
        s = stats.get(uid, {})
        total = s.get("total", 0)
        hires = s.get("hires", 0)
        avg_score = s.get("avg_score")
        rows.append({
            "recruiter_id": uid,
            "recruiter_name": user.full_name or user.email.split("@")[0],
            "email": user.email,
            "employee_id": user.employee_id or "",
            "department": user.department or "",
            "assigned_active_jds": len(entry["assigned_jds"]),
            "total_candidates": total,
            "candidates_in_interview": s.get("interviews", 0),
            "total_hires": hires,
            "average_score": round(avg_score, 1) if avg_score is not None else 0,
            "conversion_rate": pct(hires, total),
        })
    rows.sort(key=lambda r: (-r["total_hires"], -r["total_candidates"], r["recruiter_name"]))
    return rows


def job_performance_rows(jobs, applications):
    """One row per active JD with pipeline metrics."""
    stats = {
        row["job_id"]: row
        for row in applications.values("job_id").annotate(
            total=Count("id"),
            interviews=Count("id", filter=INTERVIEW_Q),
            hires=Count("id", filter=HIRED_Q),
            avg_score=Avg("score"),
        )
    }
    rows = []
    for job in jobs.prefetch_related("assigned_recruiters").order_by("-created_at"):
        s = stats.get(job.id, {})
        avg_score = s.get("avg_score")
        total = s.get("total", 0)
        hires = s.get("hires", 0)
        rows.append({
            "jd_id": job.id,
            "job_title": job.title,
            "client": job.client.name if job.client else "",
            "department": job.department or "",
            "assigned_recruiters": [
                r.full_name or r.email.split("@")[0] for r in job.assigned_recruiters.all()
            ],
            "total_candidates": total,
            "interview_count": s.get("interviews", 0),
            "hired_count": hires,
            "average_score": round(avg_score, 1) if avg_score is not None else 0,
            "conversion_rate": pct(hires, total),
            "status": job.status,
        })
    return rows


class OverallRecruiterPerformanceView(APIView):
    """GET /api/v1/reports/overall-dashboard/recruiter-performance/"""

    permission_classes = [CanViewReports]

    def get(self, request):
        filters, jobs, applications = _scope(request)
        return success_response({"recruiters": recruiter_performance_rows(filters, jobs, applications)})


class OverallJobPerformanceView(APIView):
    """GET /api/v1/reports/overall-dashboard/job-performance/"""

    permission_classes = [CanViewReports]

    def get(self, request):
        _, jobs, applications = _scope(request)
        return success_response({"jobs": job_performance_rows(jobs, applications)})


class TopPerformingJobsView(APIView):
    """GET /api/v1/reports/overall-dashboard/top-performing-jobs/ — top 5 by
    candidates, hires and conversion rate."""

    permission_classes = [CanViewReports]

    def get(self, request):
        _, jobs, applications = _scope(request)
        rows = job_performance_rows(jobs, applications)
        with_candidates = [r for r in rows if r["total_candidates"] > 0]
        return success_response({
            "by_candidates": sorted(rows, key=lambda r: -r["total_candidates"])[:5],
            "by_hires": sorted(rows, key=lambda r: -r["hired_count"])[:5],
            "by_conversion": sorted(with_candidates, key=lambda r: -r["conversion_rate"])[:5],
        })


from .views import BaseExportView  # noqa: E402  (avoids a circular import at module top)


class OverallExportView(BaseExportView):
    """GET /api/v1/reports/overall-dashboard/export/?format=csv|excel|pdf
    &section=job-performance|recruiter-performance"""

    def get(self, request):
        filters, jobs, applications = _scope(request)
        section = (request.query_params.get("section") or "job-performance").lower()

        if section == "recruiter-performance":
            headers = [
                "Recruiter", "Employee ID", "Department", "Assigned Active JDs",
                "Total Candidates", "In Interview", "Total Hires", "Avg Score", "Conversion Rate",
            ]
            rows = [
                [
                    r["recruiter_name"], r["employee_id"], r["department"],
                    r["assigned_active_jds"], r["total_candidates"], r["candidates_in_interview"],
                    r["total_hires"], r["average_score"], f"{r['conversion_rate']}%",
                ]
                for r in recruiter_performance_rows(filters, jobs, applications)
            ]
            return self.send_export(request, headers, rows, "recruiter_performance")

        headers = [
            "JD ID", "Job Title", "Client", "Department", "Assigned Recruiters",
            "Total Candidates", "Interviews", "Hired", "Avg Score", "Conversion Rate", "Status",
        ]
        rows = [
            [
                r["jd_id"], r["job_title"], r["client"], r["department"],
                ", ".join(r["assigned_recruiters"]), r["total_candidates"],
                r["interview_count"], r["hired_count"], r["average_score"],
                f"{r['conversion_rate']}%", r["status"],
            ]
            for r in job_performance_rows(jobs, applications)
        ]
        return self.send_export(request, headers, rows, "job_performance")
