'use client';

import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import DashboardRefreshBar from './DashboardRefreshBar';
import { useRouter, useSearchParams } from 'next/navigation';
import { api } from '@/lib/api';
import type { User } from '@/types';
import MyAssignedJDs, { type AssignedJD, type JdBreakdown } from '@/components/MyAssignedJDs';
import SimpleSelect from '@/components/SimpleSelect';
import ClientFilterSelect from '@/components/dashboard/ClientFilterSelect';
import ActionPointsCard, { type ActionPoint } from '@/components/dashboard/ActionPointsCard';
import RejectionAnalyticsCard, { type RejectionAnalytics } from '@/components/dashboard/RejectionAnalyticsCard';
// Shared filter chrome, so this bar is visually identical to the collapsible
// filters in the Recruiter-wise Work (Your JDs) table.
import {
  BTN as TOOLBAR_BTN,
  CLEAR_FILTERS_BTN,
  FILTER_COUNT_BADGE,
  FILTER_INPUT,
  FILTER_LABEL,
  FILTER_LABEL_TEXT,
  FILTER_PANEL,
} from '@/components/data-table/DataTableToolbar';

/** Apply button — brand-primary sibling of the toolbar's control buttons. */
const APPLY_BTN =
  'flex items-center gap-2 px-3.5 py-2.5 rounded-none text-xs font-bold bg-[#405189] border border-[#405189] ' +
  'text-white hover:bg-[#364574] transition cursor-pointer';

/** Per-field ✕, matching the plain Clear button in the Recruiter-wise Work header. */
const CLEAR_ONE_BTN =
  'text-xs px-2.5 py-1.5 border border-slate-200 dark:border-slate-800 text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800 transition cursor-pointer shrink-0';

interface RecruiterStats {
  assigned_jds: number;
  in_process: number;
  ai_screening: number;
  submitted: number;
  shortlisted: number;
  offered: number;
  joined: number;
  offer_rejected: number;
  rejected: number;
  shortlisting_offer_ratio: number;
  offer_join_ratio: number;
  offered_drop_ratio: number;
  shortlisted_ratio: number;
  avg_tat_days: number;
  fastest_tat: number;
  slowest_tat: number;
}

/** Quick Overview + Action Points for the logged-in recruiter. */
interface RecruiterOverview {
  summary: {
    assigned_jds: number;
    assigned_jds_ids: number[];
    active_jds: number;
    active_jds_ids: number[];
    on_hold_jds: number;
    on_hold_jds_ids: number[];
    closed_jds: number;
    closed_jds_ids: number[];
    screened_candidates: number;
    screened_candidates_ids: number[];
    pipeline_candidates: number;
    pipeline_candidates_ids: number[];
  };
  jd_metrics?: {
    total_received: number;
    ai_screened: number;
    shortlisted: number;
    submitted: number;
    interviewed: number;
    offered: number;
    joined: number;
    rejected: number;
    remaining: number;
  };
  /** Candidate ids behind each `jd_metrics` count, keyed identically — lets every
   *  metric card drill through to /candidates. Same sets the counts come from. */
  jd_metrics_ids?: Record<string, number[]>;
  /** Omitted by the API for users without the Candidates View permission. */
  rejection_analytics?: RejectionAnalytics;
  /** Already RBAC-filtered server-side; each point carries its View permissions. */
  action_points: ActionPoint[];
  performance?: Record<string, { count: number; ids: number[] }>;
  /** Per-JD opening progress + funnel, consumed by the My Assigned JDs table. */
  jd_breakdown?: JdBreakdown[];
}

/** Everything GET /dashboard/recruiter/all/ returns, in one response. */
interface RecruiterDashboardAll {
  stats: RecruiterStats | null;
  overview: RecruiterOverview | null;
  assigned_jds: AssignedJD[];
  recruiter_work: unknown[];
  clients: { id: number; name: string }[];
  filters: Record<string, string>;
}

/** The filter set the dashboard sends. `client` maps to `client_id` on the API. */
interface DashboardFilters {
  jd: string;
  client: string;
  fromDate: string;
  toDate: string;
}

const EMPTY_FILTERS: DashboardFilters = { jd: '', client: '', fromDate: '', toDate: '' };

/** Serialise filters into the consolidated endpoint's query string. Stable
 *  output for equal input, which is what stops redundant re-fetches. */
function buildQuery(f: DashboardFilters): string {
  const params = new URLSearchParams();
  if (f.jd) params.append('jd_id', f.jd);
  if (f.client) params.append('client_id', f.client);
  if (f.fromDate) params.append('from_date', f.fromDate);
  if (f.toDate) params.append('to_date', f.toDate);
  const qs = params.toString();
  return qs ? `?${qs}` : '';
}

const sameFilters = (a: DashboardFilters, b: DashboardFilters) => buildQuery(a) === buildQuery(b);

// Module-scope cache — lets revisits paint instantly while refreshing silently.
let statsCache: RecruiterStats | null = null;
let overviewCache: RecruiterOverview | null = null;

export default function RecruiterDashboard({ user }: { user: User }) {
  const router = useRouter();
  const searchParams = useSearchParams();
  const selectedClient = searchParams.get('client') || '';
  const [data, setData] = useState<RecruiterStats | null>(statsCache);
  const [overview, setOverview] = useState<RecruiterOverview | null>(overviewCache);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  // ---- Filters -------------------------------------------------------------
  // Two layers, so nothing is fetched while the user is still choosing:
  //   draft   — what the inputs currently show
  //   applied — what the last Apply click sent, and the only thing that fetches
  // JD filter '' = All Job Descriptions. Only the recruiter's own assigned JDs
  // are listed, and the backend re-scopes every metric (never a UI-only filter).
  const [draft, setDraft] = useState<DashboardFilters>({ ...EMPTY_FILTERS, client: selectedClient });
  const [applied, setApplied] = useState<DashboardFilters>({ ...EMPTY_FILTERS, client: selectedClient });

  // Everything the dashboard renders, from one call.
  const [assignedJds, setAssignedJds] = useState<AssignedJD[]>([]);
  const [clients, setClients] = useState<{ id: number; name: string }[]>([]);

  // Options for the JD dropdown come from the same consolidated response.
  const myJds = useMemo(
    () => assignedJds.map((j) => ({ jd_id: j.jd_id, title: j.title })),
    [assignedJds],
  );

  // Filter popover state and refs
  const [isFilterOpen, setIsFilterOpen] = useState(false);
  const filterRef = useRef<HTMLDivElement>(null);
  const filterButtonRef = useRef<HTMLButtonElement>(null);

  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      const target = event.target as Element | null;
      if (!target) return;
      if (!document.body.contains(target)) return;
      if (filterButtonRef.current && filterButtonRef.current.contains(target)) return;
      if (filterRef.current && filterRef.current.contains(target)) return;
      if (target.closest('.searchable-select-menu') || target.closest('[class*="-menu"]')) return;
      setIsFilterOpen(false);
    };
    if (isFilterOpen) {
      document.addEventListener('mousedown', handleClickOutside);
    }
    return () => {
      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [isFilterOpen]);

  // ONE request for the whole dashboard. Every section below — stats, overview,
  // action points, rejection analytics, assigned JDs, the JD dropdown and the
  // client dropdown — is populated from this single response. `applied` is the
  // only dependency, so choosing a value in the panel fetches nothing; Apply
  // does. The individual endpoints still exist and are untouched.
  const query = useMemo(() => buildQuery(applied), [applied]);
  /** Query string of the last effect-driven fetch — see the guard below. */
  const lastQueryRef = useRef<string | null>(null);

  const loadData = useCallback(() => {
    setLoading(true);
    api.get(`/dashboard/recruiter/all/${query}`)
      .then((res: unknown) => {
        const payload = (res as { data?: RecruiterDashboardAll })?.data;
        if (!payload) throw new Error('Empty dashboard response');
        statsCache = payload.stats ?? null;
        overviewCache = payload.overview ?? null;
        setData(statsCache);
        setOverview(overviewCache);
        setAssignedJds(payload.assigned_jds ?? []);
        setClients(payload.clients ?? []);
        setError('');
      })
      .catch((err) => {
        console.error('Failed to load recruiter dashboard:', err);
        setError('Failed to load performance metrics.');
      })
      .finally(() => {
        setLoading(false);
      });
  }, [query]);

  useEffect(() => {
    // Fetch once per distinct filter set. Guards against React StrictMode's
    // double effect invocation in dev and against any re-render that produces
    // the same query — so Apply with unchanged values sends nothing. Explicit
    // refreshes below deliberately bypass this by calling loadData() directly.
    if (lastQueryRef.current !== query) {
      lastQueryRef.current = query;
      loadData();
    }

    // Listen for WebSocket notifications to trigger real-time updates
    const handleNotification = () => {
      loadData();
    };

    window.addEventListener('notification-received', handleNotification);
    // Recruiter activity elsewhere in the app (JD/pipeline events) also
    // refreshes the overview, reusing the existing ws-event broadcast.
    const handleWs = (e: Event) => {
      const name = (e as CustomEvent).detail?.event;
      if (['jd_submitted', 'jd_approved', 'jd_rejected', 'jd_pending_approval'].includes(name)) {
        loadData();
      }
    };
    window.addEventListener('ws-event', handleWs);
    return () => {
      window.removeEventListener('notification-received', handleNotification);
      window.removeEventListener('ws-event', handleWs);
    };
  }, [loadData, query]);

  /** "All Job Descriptions" + this recruiter's own assigned JDs.
   *  Declared before the early returns below — hooks must run in the same
   *  order on every render. */
  const jdOptions = useMemo(
    () => [
      { value: '', label: 'All Job Descriptions' },
      ...myJds.map((j) => ({
        value: String(j.jd_id),
        label: `JD-${String(j.jd_id).padStart(4, '0')} · ${j.title}`,
      })),
    ],
    [myJds],
  );

  /** Label of the JD the DATA is currently scoped to — driven by the applied
   *  filter, not the draft, so section headings always describe what is shown. */
  const selectedJdLabel = applied.jd
    ? (myJds.find((j) => String(j.jd_id) === applied.jd)?.title ?? `JD-${applied.jd.padStart(4, '0')}`)
    : '';

  if (loading && !data) {
    return (
      <div className="flex items-center justify-center min-h-[300px]">
        <div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-[#405189]"></div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="bg-red-50 dark:bg-red-950/20 border border-red-200 dark:border-red-800 p-4 rounded-none text-red-700 dark:text-red-300 text-sm">
        {error}
      </div>
    );
  }

  if (!data) return null;

  /** Build a drill-down URL pinned to the exact ids behind a card's count.
   *  `extra` adds further query params — e.g. `{ focus: '1' }` opens the
   *  destination page in its distraction-free "just the list" mode. */
  const drillTo = (base: string, ids: number[], label: string, extra?: Record<string, string>) =>
    `${base}?ids=${ids.join(',')}&label=${encodeURIComponent(label)}${extra ? `&${new URLSearchParams(extra).toString()}` : ''}`;

  const perf = overview?.performance;

  /** One card per pipeline bucket in `overview.performance` — these ship the
   *  matching candidate ids, so they stay clickable. Value falls back to the
   *  legacy stats endpoint when the overview isn't available (unchanged). */
  const perfCard = (key: string, label: string, icon: string, color: string, hint: string) => {
    const item = perf?.[key];
    const legacy = data ? data[key as keyof RecruiterStats] : 0;
    const value = item?.count ?? (typeof legacy === 'number' ? legacy : 0);
    return {
      key, label, icon, color, hint, value,
      route: drillTo('/candidates', item?.ids ?? [], label),
    };
  };

  /** A card sourced from `overview.jd_metrics`. The API now also returns the
   *  candidate ids behind each of those counts (`jd_metrics_ids`), so these
   *  drill through to the same /candidates list the `performance` cards use —
   *  every metric card is clickable. */
  const jdCard = (
    key: string, label: string, icon: string, color: string, hint: string,
    extra?: Record<string, string>,
  ) => ({
    key, label, icon, color, hint,
    value: overview?.jd_metrics?.[key as keyof NonNullable<typeof overview.jd_metrics>] ?? 0,
    route: drillTo('/candidates', overview?.jd_metrics_ids?.[key] ?? [], label, extra),
  });

  /**
   * Single consolidated metrics list, replacing the two sections that used to
   * render "Recruitment Metrics (All JDs)" and "Recruiter Performance Metrics".
   *
   * Both were already built from ONE API response (/dashboard/recruiter/overview/),
   * and four buckets were literally the same number — the backend computes
   * jd_metrics.submitted/shortlisted/offered/joined *from* performance[...]. Those
   * appear once here, taking the performance variant so the drill-through to the
   * candidate list is kept.
   *
   * The rest are NOT duplicates and are all preserved, because they answer
   * different questions:
   *   • AI Screened  — candidates that have a rank score (jd_metrics)
   *   • AI Screening — candidates currently sitting in an AI calling/screened
   *                    stage (performance)
   *   • Rejected     — every LOST-outcome stage (jd_metrics), which is the sum of
   *                    performance.rejected + performance.offer_rejected
   *   • Offer Rejected — the offer-declined half of that split (performance)
   * No metric, count or aggregation was changed; this only decides what renders
   * where. `hint` becomes a tooltip so two similar labels can't be misread.
   */
  const metricCards = [
    jdCard('total_received', 'Total Candidates Received', 'fa-solid fa-inbox', 'bg-blue-500/10 text-blue-600',
      'Distinct candidates who applied to your assigned JDs.'),
    jdCard('ai_screened', 'AI Screened', 'fa-solid fa-wand-magic-sparkles', 'bg-indigo-500/10 text-indigo-600',
      'Candidates that have an AI rank score on your JDs.'),
    perfCard('in_process', 'In Process', 'fa-solid fa-spinner fa-spin-slow', 'bg-yellow-500/10 text-yellow-500',
      'Candidates in any active (in-progress) pipeline stage.'),
    perfCard('ai_screening', 'AI Screening', 'fa-solid fa-phone-volume', 'bg-rose-500/10 text-rose-500',
      'Candidates currently in an AI calling / AI screened stage.'),
    perfCard('submitted', 'Submitted', 'fa-solid fa-arrow-up-from-bracket', 'bg-purple-500/10 text-purple-500',
      'Candidates submitted to the client.'),
    perfCard('shortlisted', 'Shortlisted', 'fa-solid fa-circle-check', 'bg-green-500/10 text-green-500',
      'Candidates selected/shortlisted.'),
    jdCard('interviewed', 'Interviewed', 'fa-solid fa-comments', 'bg-cyan-500/10 text-cyan-600',
      'Candidates in the interviewing stage.'),
    perfCard('offered', 'Offered', 'fa-solid fa-file-signature', 'bg-teal-500/10 text-teal-500',
      'Candidates who received an offer.'),
    perfCard('joined', 'Joined', 'fa-solid fa-user-check', 'bg-indigo-500/10 text-indigo-500',
      'Candidates placed / joined.'),
    perfCard('offer_rejected', 'Offer Rejected', 'fa-solid fa-ban', 'bg-orange-500/10 text-orange-500',
      'Offer declined, or offered but did not join.'),
    // `focus: '1'` opens the Candidates page in its distraction-free mode —
    // just the title + filtered list, no Bulk Candidate/Bulk Notification/
    // Inactive Candidates/Add Candidate toolbar — so a recruiter clicking in
    // to see who was rejected isn't pulled into unrelated bulk workflows.
    jdCard('rejected', 'Rejected Candidates', 'fa-solid fa-user-xmark', 'bg-rose-500/10 text-rose-600',
      'Every candidate in a lost-outcome stage (includes offer rejections).', { focus: '1' }),
    jdCard('remaining', 'Remaining Candidates', 'fa-solid fa-hourglass-half', 'bg-amber-500/10 text-amber-600',
      'Received candidates not yet shortlisted or beyond.'),
  ];

  const ratioCards = [
    { label: 'Shortlisting to Offer', value: `${data.shortlisting_offer_ratio}%`, desc: 'Offered / Shortlisted candidates' },
    { label: 'Offer to Join', value: `${data.offer_join_ratio}%`, desc: 'Joined / Offered candidates' },
    { label: 'Offered Drop', value: `${data.offered_drop_ratio}%`, desc: 'Rejected Offer / Offered candidates' },
    { label: 'Shortlisted Ratio', value: `${data.shortlisted_ratio}%`, desc: 'Shortlisted / Submitted candidates' },
  ];

  const s = overview?.summary;
  const quickCards = s ? [
    { label: 'Assigned JDs', value: s.assigned_jds, icon: 'fa-solid fa-briefcase', color: 'bg-blue-500/10 text-blue-600', route: drillTo('/jobs', s.assigned_jds_ids, 'Assigned JDs') },
    { label: 'Active JDs', value: s.active_jds, icon: 'fa-solid fa-bolt', color: 'bg-emerald-500/10 text-emerald-600', route: drillTo('/jobs', s.active_jds_ids, 'Active JDs') },
    { label: 'On Hold JDs', value: s.on_hold_jds ?? 0, icon: 'fa-solid fa-circle-pause', color: 'bg-amber-500/10 text-amber-600', route: drillTo('/jobs', s.on_hold_jds_ids ?? [], 'On Hold JDs') },
    { label: 'Closed JDs', value: s.closed_jds ?? 0, icon: 'fa-solid fa-box-archive', color: 'bg-slate-500/10 text-slate-600', route: drillTo('/jobs', s.closed_jds_ids ?? [], 'Closed JDs') },
    // Candidate-level metrics open the CANDIDATE list (previously they opened /jobs).
    { label: 'Screened Candidates', value: s.screened_candidates, icon: 'fa-solid fa-wand-magic-sparkles', color: 'bg-indigo-500/10 text-indigo-600', route: drillTo('/candidates', s.screened_candidates_ids, 'Screened Candidates') },
    { label: 'Pipeline Candidates', value: s.pipeline_candidates, icon: 'fa-solid fa-diagram-project', color: 'bg-violet-500/10 text-violet-600', route: drillTo('/candidates', s.pipeline_candidates_ids, 'Pipeline Candidates') },
  ] : [];

  // Badge counts the filters actually in effect (same four as before).
  const activeFilterCount = [applied.jd, applied.client, applied.fromDate, applied.toDate]
    .filter(Boolean).length;
  /** True while the panel holds edits that have not been applied yet. */
  const isDirty = !sameFilters(draft, applied);

  /** Send the drafted filters — the single point that triggers a fetch.
   *  Applying an unchanged set is a no-op: `applied` keeps its value, the query
   *  string is identical, and no request goes out. */
  const applyFilters = () => {
    if (isDirty) setApplied({ ...draft });
    setIsFilterOpen(false);
  };

  /** Reset every filter at once and fetch the unfiltered dashboard. */
  const clearAllFilters = () => {
    setDraft({ ...EMPTY_FILTERS });
    if (!sameFilters(applied, EMPTY_FILTERS)) setApplied({ ...EMPTY_FILTERS });
  };

  return (
    <div className="space-y-6">
      {/* ===== Filters — same UI as the Recruiter-wise Work (Your JDs) section
          (DataTableToolbar's collapsible filters + that section's date-range
          controls). Every field, its state and its data-loading behaviour are
          unchanged; only the chrome now matches. ===== */}
      <div className="space-y-3">
        <div className="flex flex-col md:flex-row gap-3 items-center justify-end">
          <div className="flex flex-wrap gap-3 w-full md:w-auto items-center justify-start md:justify-end">
            <DashboardRefreshBar onRefresh={loadData} />
            {loading && (
              <span className="text-[11px] font-semibold text-[#405189] dark:text-indigo-300">
                <i className="fa-solid fa-spinner fa-spin mr-1.5"></i>Updating…
              </span>
            )}

            <button
              ref={filterButtonRef}
              type="button"
              onClick={() => setIsFilterOpen((prev) => !prev)}
              className={TOOLBAR_BTN}
            >
              <i className="fa-solid fa-sliders"></i>
              Filters
              {activeFilterCount > 0 && (
                <span className={FILTER_COUNT_BADGE}>{activeFilterCount}</span>
              )}
              <i className={`fa-solid fa-chevron-${isFilterOpen ? 'up' : 'down'} text-[10px]`}></i>
            </button>

            {activeFilterCount > 0 && (
              <button type="button" onClick={clearAllFilters} className={CLEAR_FILTERS_BTN}>
                <i className="fa-solid fa-xmark"></i> Clear Filters
              </button>
            )}
          </div>
        </div>

        {isFilterOpen && (
          <div ref={filterRef} className={FILTER_PANEL}>
            <div className="flex flex-wrap gap-3 items-end">
              {/* Job Description — label text only carries the caption classes;
                  `uppercase`/`tracking-wider` would otherwise cascade into the
                  select and shout the JD titles. */}
              <div className="flex items-end gap-1.5">
                <div className="flex flex-col">
                  <span className={FILTER_LABEL_TEXT}>Job Description</span>
                  <div className="mt-1 w-full sm:w-64 min-w-0">
                    <SimpleSelect
                      options={jdOptions}
                      value={draft.jd}
                      onChange={(v: unknown) => {
                        const next = typeof v === 'object' && v !== null
                          ? String((v as { value?: string | number }).value ?? '')
                          : String(v ?? '');
                        setDraft((d) => ({ ...d, jd: next }));
                      }}
                      placeholder="All Job Descriptions"
                    />
                  </div>
                </div>
                {draft.jd && (
                  <button
                    type="button"
                    onClick={() => setDraft((d) => ({ ...d, jd: '' }))}
                    title="Clear Job Description"
                    className={CLEAR_ONE_BTN}
                  >
                    <i className="fa-solid fa-xmark"></i>
                  </button>
                )}
              </div>

              {/* From Date — same label/input pair as the Recruiter-wise Work
                  date range, clear button as a sibling of the label. */}
              <div className="flex items-end gap-1.5">
                <label className={FILTER_LABEL}>
                  From
                  <input
                    type="date"
                    value={draft.fromDate}
                    max={draft.toDate || undefined}
                    onChange={(e) => setDraft((d) => ({ ...d, fromDate: e.target.value }))}
                    className={FILTER_INPUT}
                  />
                </label>
                {draft.fromDate && (
                  <button
                    type="button"
                    onClick={() => setDraft((d) => ({ ...d, fromDate: '' }))}
                    title="Clear From Date"
                    className={CLEAR_ONE_BTN}
                  >
                    <i className="fa-solid fa-xmark"></i>
                  </button>
                )}
              </div>

              {/* To Date */}
              <div className="flex items-end gap-1.5">
                <label className={FILTER_LABEL}>
                  To
                  <input
                    type="date"
                    value={draft.toDate}
                    min={draft.fromDate || undefined}
                    onChange={(e) => setDraft((d) => ({ ...d, toDate: e.target.value }))}
                    className={FILTER_INPUT}
                  />
                </label>
                {draft.toDate && (
                  <button
                    type="button"
                    onClick={() => setDraft((d) => ({ ...d, toDate: '' }))}
                    title="Clear To Date"
                    className={CLEAR_ONE_BTN}
                  >
                    <i className="fa-solid fa-xmark"></i>
                  </button>
                )}
              </div>

              {/* Client — options come from the consolidated response (no extra
                  request), and `onChange` keeps it an in-place filter so picking
                  a client doesn't fetch or navigate until Apply. The component's
                  own RBAC still applies: it renders nothing when there are no
                  clients the user may list. */}
              <div className="flex items-end gap-1.5">
                <div className="flex flex-col">
                  <span className={FILTER_LABEL_TEXT}>Client</span>
                  <div className="mt-1">
                    <ClientFilterSelect
                      clients={clients}
                      clientId={draft.client}
                      onChange={(id) => setDraft((d) => ({ ...d, client: id }))}
                    />
                  </div>
                </div>
                {draft.client && (
                  <button
                    type="button"
                    onClick={() => setDraft((d) => ({ ...d, client: '' }))}
                    title="Clear Client"
                    className={CLEAR_ONE_BTN}
                  >
                    <i className="fa-solid fa-xmark"></i>
                  </button>
                )}
              </div>

              {/* Apply — the only control that fires a request. */}
              <button
                type="button"
                onClick={applyFilters}
                disabled={!isDirty}
                title={isDirty ? 'Apply these filters' : 'No filter changes to apply'}
                className={`${APPLY_BTN} ${isDirty ? '' : 'opacity-50 cursor-not-allowed hover:bg-[#405189]'}`}
              >
                <i className="fa-solid fa-check"></i> Apply
              </button>
            </div>
          </div>
        )}
      </div>

      {/* ===== Quick Overview (left) + Action Points (top-right) =====
          The first two sections on the dashboard. Same cards, data and
          drill-down routes as before — they share one row so the Action Points
          panel sits in the dashboard's top-right corner. Stacks to full width
          below xl. */}
      {/* `items-stretch` (the grid default) lets the Action Points column fill
          the row, so it ends level with the Quick Overview cards. The row's
          height still comes from Quick Overview alone — the card's list carries
          `min-h-0`, so its content can never stretch the row. */}
      {overview && (
        <div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-stretch">
          <div className="xl:col-span-2">
            <div className="mb-3">
              <h3 className="text-base font-bold text-[#495057] dark:text-white uppercase tracking-wider">Quick Overview</h3>
              <p className="text-xs text-vz-muted mt-0.5">Your day at a glance — scoped to the JDs assigned to you.</p>
            </div>
            <div className="grid grid-cols-2 md:grid-cols-3 gap-3">
              {quickCards.map((c) => (
                <button
                  key={c.label}
                  type="button"
                  onClick={() => window.open(c.route, '_blank', 'noopener,noreferrer')}
                  className="text-left bg-white dark:bg-slate-900 border border-vz-border dark:border-slate-800 p-4 shadow-sm hover:translate-y-[-2px] hover:border-[#405189] cursor-pointer transition duration-200"
                >
                  <div className="flex items-start justify-between gap-2">
                    <p className="text-[10px] uppercase tracking-wider text-vz-muted font-bold">{c.label}</p>
                    <span className={`w-8 h-8 rounded-full flex items-center justify-center text-xs shrink-0 ${c.color}`}>
                      <i className={c.icon}></i>
                    </span>
                  </div>
                  <p className="text-2xl font-black text-[#495057] dark:text-white mt-2">{c.value}</p>
                </button>
              ))}
            </div>
          </div>

          {/* headingOutside lifts the title alongside "Quick Overview" so both
              panels' top edges line up exactly; fillHeight makes the panel end
              level with the Quick Overview cards, scrolling anything that
              doesn't fit. */}
          <ActionPointsCard
            points={overview.action_points}
            loading={loading}
            headingOutside
            fillHeight
            subtitle="Pending items on your assigned JDs."
            emptyDescription="No pending actions on your assigned JDs right now."
            className="xl:col-span-1"
          />
        </div>
      )}

      {/* Overview Title — the two metrics sections are now one. Keeps the
          JD-filter-aware wording the merged-in section used, so choosing a JD
          still relabels the heading exactly as it did before. */}
      <div>
        <h3 className="text-base font-bold text-[#495057] dark:text-white uppercase tracking-wider">
          {selectedJdLabel ? 'JD Recruitment Metrics' : 'Recruitment Metrics (All JDs)'}
        </h3>
        <p className="text-xs text-vz-muted mt-0.5">
          {selectedJdLabel
            ? `Candidate funnel for ${selectedJdLabel}.`
            : 'Real-time candidate funnel across all your assigned Job Descriptions.'}
        </p>
      </div>

      {/* Metrics Grid — auto-reflowing responsive grid, no fixed slot count.
          EVERY card drills through to the existing candidate list, pinned to the
          exact records behind its count: `performance` cards use their own id
          list, `jd_metrics` cards use the matching `jd_metrics_ids` entry. Same
          page, same filters, same API — no new route. */}
      <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-3">
        {metricCards.map((card) => (
          <button
            key={card.label}
            type="button"
            onClick={() => window.open(card.route, '_blank', 'noopener,noreferrer')}
            title={`${card.hint} Click to show these ${card.value} record(s).`}
            className="text-left bg-white dark:bg-slate-900 border border-vz-border dark:border-slate-800 p-3 shadow-sm flex flex-col justify-between hover:translate-y-[-2px] hover:border-[#405189] cursor-pointer transition duration-200"
          >
            <div className="flex justify-between items-start gap-1">
              <p className="text-[9px] uppercase tracking-wider text-vz-muted font-bold truncate">{card.label}</p>
              <span className={`w-6 h-6 rounded-full flex items-center justify-center text-[10px] shrink-0 ${card.color}`}>
                <i className={card.icon}></i>
              </span>
            </div>
            <p className="text-lg font-bold text-[#495057] dark:text-white mt-2">{card.value}</p>
          </button>
        ))}
      </div>

      {/* ===== Rejection Analytics ===== */}
      {/* Hidden as of now, per request — commented out rather than removed so
          it can be switched back on by uncommenting this block. Sits directly
          below Recruitment Metrics. Same table, wording and data —
          fixed-height/scrollable, paginated with the shared DataTablePagination,
          and collapsible. Renders nothing without the Candidates View permission
          (the backend also withholds it).
      <RejectionAnalyticsCard
        data={overview?.rejection_analytics}
        scopeLabel={selectedJdLabel}
      />
      */}

      {/* Quick Overview + Action Points render at the very top of the dashboard
          (immediately under the filter toolbar) — see above. */}

      {/* Ratios & TAT Cards */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
        
        {/* Conversion Ratios */}
        <div className="lg:col-span-2 bg-white dark:bg-slate-900 border border-vz-border dark:border-slate-800 p-5 shadow-sm space-y-4">
          <div>
            <h4 className="text-sm font-bold text-[#495057] dark:text-white uppercase tracking-wider">Conversion & Drop Ratios</h4>
            <p className="text-[10px] text-vz-muted mt-0.5">Historical pipeline conversions and offer drops.</p>
          </div>
          <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
            {ratioCards.map((ratio) => (
              <div key={ratio.label} className="bg-slate-50 dark:bg-slate-950 p-3.5 border border-vz-border dark:border-slate-800">
                <p className="text-[9px] uppercase tracking-wider text-vz-muted font-bold">{ratio.label}</p>
                <p className="text-xl font-extrabold text-[#405189] dark:text-indigo-400 mt-2">{ratio.value}</p>
                <p className="text-[9px] text-slate-400 dark:text-slate-500 mt-1 leading-tight">{ratio.desc}</p>
              </div>
            ))}
          </div>
        </div>

        {/* Turnaround Time (TAT) */}
        <div className="bg-white dark:bg-slate-900 border border-vz-border dark:border-slate-800 p-5 shadow-sm space-y-4">
          <div>
            <h4 className="text-sm font-bold text-[#495057] dark:text-white uppercase tracking-wider">Turnaround Time (TAT)</h4>
            <p className="text-[10px] text-vz-muted mt-0.5">Average, fastest, and slowest days from start to final status.</p>
          </div>
          <div className="grid grid-cols-3 gap-2">
            
            <div className="bg-[#405189]/5 border border-[#405189]/10 p-3 text-center">
              <p className="text-[9px] uppercase text-[#405189] font-bold">Average</p>
              <p className="text-lg font-black text-[#405189] mt-2">{data.avg_tat_days}</p>
              <p className="text-[8px] text-[#405189]/70 mt-1 font-semibold">Days</p>
            </div>

            <div className="bg-green-500/5 border border-green-500/10 p-3 text-center">
              <p className="text-[9px] uppercase text-green-500 font-bold">Fastest</p>
              <p className="text-lg font-black text-green-650 dark:text-green-450 mt-2">{data.fastest_tat}</p>
              <p className="text-[8px] text-green-500/70 mt-1 font-semibold">Days</p>
            </div>

            <div className="bg-orange-500/5 border border-orange-500/10 p-3 text-center">
              <p className="text-[9px] uppercase text-orange-500 font-bold">Slowest</p>
              <p className="text-lg font-black text-orange-600 dark:text-orange-400 mt-2">{data.slowest_tat}</p>
              <p className="text-[8px] text-orange-500/70 mt-1 font-semibold">Days</p>
            </div>

          </div>
        </div>

      </div>

      {/* Assigned JDs table (stat mini-cards + full table) */}
      {/* Fed from the same consolidated response — it makes no requests here. */}
      <MyAssignedJDs
        jds={assignedJds}
        jdBreakdown={overview?.jd_breakdown ?? []}
        loading={loading && !data}
      />
    </div>
  );
}
