"""Talent Acquisition dashboard — org-wide requirement & pipeline analytics.

Additive endpoints under /api/dashboard/ta/ (and /api/v1/dashboard/ta/):

    requirements-summary/?period=monthly|quarterly|half_yearly|annually
    pending-requirements/
    recruiter-jd-assignment/
    pipeline-summary/?period=...[&detail=<metric>]

Period windows are calendar-aligned (month / quarter / half-year / year);
the percentage change compares against the immediately preceding window.
Stage metrics reuse the PM dashboard classifier (pm_views), plus:

    hold  →  applications whose interview_result is "On Hold"
"""

from datetime import date

from rest_framework.views import APIView

from core.responses import error_response, success_response

from apps.jobs.models import JDRecruiterAssignment, JobDescription
from apps.pipeline.models import JobApplication, PipelineStage
from apps.reports.permissions import CanViewReports

from .pm_views import METRIC_KEYS, _classify
from .period_utils import PERIODS, parse_period as _parse_period, period_window as _period_window


def _pending_jds():
    """Requirements still needing work: not Closed and nobody joined yet.
    Sub-status is derived (the JD model has no Open/Pending Assignment states):
    Draft -> Open, no recruiters -> Pending Assignment, else In Progress."""
    joined_job_ids = set(
        JobApplication.objects.filter(stage__outcome=PipelineStage.Outcome.WON)
        .values_list("job_id", flat=True)
    )
    rows = []
    for job in (
        JobDescription.objects.exclude(status="Closed")
        .select_related("client").prefetch_related("assigned_recruiters")
        .order_by("-created_at")
    ):
        if job.id in joined_job_ids:
            continue
        recruiters = [r.full_name or r.email.split("@")[0] for r in job.assigned_recruiters.all()]
        if job.status == "Draft":
            pending_status = "Open"
        elif not recruiters:
            pending_status = "Pending Assignment"
        else:
            pending_status = "In Progress"
        rows.append({
            "jd_id": job.id,
            "jd_title": job.title,
            "client_name": job.client.name if job.client else "",
            "department": job.department or "",
            "created_date": job.created_at.strftime("%Y-%m-%d"),
            "recruiters": recruiters,
            "pending_status": pending_status,
        })
    return rows


def _trend_buckets(period, start):
    """(ordered labels, date -> label) for the bar-chart breakdown of a window:
    monthly -> weeks, quarterly -> months, half_yearly -> quarters, annually -> halves."""
    if period == "quarterly":
        labels = [date(start.year, start.month + i, 1).strftime("%b") for i in range(3)]

        def bucket(d):
            return date(d.year, d.month, 1).strftime("%b")
    elif period == "half_yearly":
        base_q = (start.month - 1) // 3
        labels = [f"Q{base_q + 1}", f"Q{base_q + 2}"]

        def bucket(d):
            return f"Q{(d.month - 1) // 3 + 1}"
    elif period == "annually":
        labels = ["H1", "H2"]

        def bucket(d):
            return "H1" if d.month <= 6 else "H2"
    else:  # monthly -> weeks of the month
        labels = ["W1", "W2", "W3", "W4", "W5"]

        def bucket(d):
            return f"W{min((d.day - 1) // 7 + 1, 5)}"
    return labels, bucket


class TARequirementsSummaryView(APIView):
    """GET /dashboard/ta/requirements-summary/?period=... — JDs added + trend."""

    permission_classes = [CanViewReports]

    def get(self, request):
        period = _parse_period(request)
        start, end, prev_start, prev_end = _period_window(period)

        current = JobDescription.objects.filter(
            created_at__date__gte=start, created_at__date__lte=end).count()
        previous = JobDescription.objects.filter(
            created_at__date__gte=prev_start, created_at__date__lt=prev_end).count()
        if previous:
            change_pct = round((current - previous) / previous * 100, 1)
        else:
            change_pct = 100.0 if current else 0.0

        return success_response({
            "period": period,
            "window_start": start.isoformat(),
            "total": current,
            "previous_period_total": previous,
            "change_pct": change_pct,
            "pending": len(_pending_jds()),
        })


class TAPendingRequirementsView(APIView):
    """GET /dashboard/ta/pending-requirements/ — the detailed pending list."""

    permission_classes = [CanViewReports]

    def get(self, request):
        rows = _pending_jds()
        return success_response({"total": len(rows), "requirements": rows})


class TARecruiterJDAssignmentView(APIView):
    """GET /dashboard/ta/recruiter-jd-assignment/ — per-JD assignment + pipeline."""

    permission_classes = [CanViewReports]

    def get(self, request):
        jobs = list(
            JobDescription.objects.select_related("client")
            .prefetch_related("assigned_recruiters")
            .order_by("-created_at")
        )
        stage_by_code = {s.code: s for s in PipelineStage.objects.filter(is_active=True)}
        thresholds = {
            "submitted": getattr(stage_by_code.get("submitted"), "sort_order", None),
            "shortlisted": getattr(stage_by_code.get("selected"), "sort_order", None),
            "offered": getattr(stage_by_code.get("offered"), "sort_order", None),
        }

        per_job = {j.id: dict.fromkeys(METRIC_KEYS, 0) for j in jobs}
        for app in JobApplication.objects.filter(
            candidate__is_deleted=False
        ).select_related("stage"):
            if app.job_id in per_job:
                for key in _classify(app.stage, thresholds):
                    per_job[app.job_id][key] += 1

        # Latest assignment per JD (who assigned, when)
        latest_assignment = {}
        for a in (
            JDRecruiterAssignment.objects.filter(jd_id__in=per_job)
            .select_related("assigned_by").order_by("assigned_at")
        ):
            latest_assignment[a.jd_id] = a

        rows = []
        for job in jobs:
            counts = per_job[job.id]
            assignment = latest_assignment.get(job.id)
            assigned_by = assignment.assigned_by if assignment else None
            rows.append({
                "jd_id": job.id,
                "jd_title": job.title,
                "client_name": job.client.name if job.client else "",
                "department": job.department or "",
                "positions": None,  # the JD model doesn't track openings count
                "recruiters": [
                    r.full_name or r.email.split("@")[0] for r in job.assigned_recruiters.all()
                ],
                "assigned_by": (assigned_by.full_name or assigned_by.email.split("@")[0]) if assigned_by else "",
                "assigned_date": assignment.assigned_at.strftime("%Y-%m-%d") if assignment else "",
                "submitted": counts["submitted"],
                "shortlisted": counts["shortlisted"],
                "offered": counts["offered"],
                "joined": counts["joined"],
                "status": job.status,
            })
        return success_response({"rows": rows})


class TAPipelineSummaryView(APIView):
    """GET /dashboard/ta/pipeline-summary/?period=...[&detail=<metric>]

    Counts (and optionally the matching records) for applications created in
    the selected period. Metrics: submitted, shortlisted, offered, joined,
    offer_rejected, rejected, hold."""

    permission_classes = [CanViewReports]

    VALID_DETAIL = set(METRIC_KEYS) | {"hold"}

    def get(self, request):
        period = _parse_period(request)
        start, end, _, _ = _period_window(period)
        detail = (request.query_params.get("detail") or "").lower()
        if detail and detail not in self.VALID_DETAIL:
            return error_response(f"Unknown metric '{detail}'.")

        stage_by_code = {s.code: s for s in PipelineStage.objects.filter(is_active=True)}
        thresholds = {
            "submitted": getattr(stage_by_code.get("submitted"), "sort_order", None),
            "shortlisted": getattr(stage_by_code.get("selected"), "sort_order", None),
            "offered": getattr(stage_by_code.get("offered"), "sort_order", None),
        }

        applications = (
            JobApplication.objects.filter(
                candidate__is_deleted=False,
                created_at__date__gte=start, created_at__date__lte=end,
            )
            .select_related("stage", "candidate", "job")
            .order_by("-updated_at")
        )

        trend_labels, bucket_of = _trend_buckets(period, start)
        trend = dict.fromkeys(trend_labels, 0)

        counts = dict.fromkeys(METRIC_KEYS, 0)
        counts["hold"] = 0
        records = []
        for app in applications:
            hit = list(_classify(app.stage, thresholds))
            if app.interview_result == "On Hold":
                hit.append("hold")
            for key in hit:
                counts[key] += 1
            label = bucket_of(app.created_at.date())
            if label in trend:
                trend[label] += 1
            if detail and detail in hit and len(records) < 100:
                records.append({
                    "candidate_id": app.candidate_id,
                    "candidate": f"{app.candidate.first_name} {app.candidate.last_name}".strip(),
                    "jd_id": app.job_id,
                    "jd_title": app.job.title,
                    "stage": app.stage.name if app.stage else "",
                    "score": app.score,
                    "added_on": app.created_at.strftime("%Y-%m-%d"),
                })

        payload = {
            "period": period, "window_start": start.isoformat(), **counts,
            "trend": [{"label": lbl, "value": trend[lbl]} for lbl in trend_labels],
        }
        if detail:
            payload["detail_metric"] = detail
            payload["records"] = records
        return success_response(payload)
