'use client';

import { useEffect, useId, useMemo, useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/components/auth-context';
import { hasAnyDjangoPermission } from '@/lib/permissions';
import SimpleSelect from '@/components/SimpleSelect';

/**
 * Action Points card — the compact "what needs my attention" panel that sits in
 * the top-right corner of the Recruiter, Project Manager and Hiring Manager
 * dashboards.
 *
 * RBAC
 * ----
 * The backend already drops every point whose target module the caller has no
 * View permission for (apps/dashboard/action_points.py), and sends the
 * permission codes that unlocked each surviving one. This component re-applies
 * the SAME check with the user's own permission list before wiring up the
 * click, so a card can never navigate to a module the role cannot open — it
 * renders disabled instead. Nothing here hardcodes a role.
 *
 * Layout
 * ------
 * `headingOutside` lifts the title/subtitle above the panel so it lines up with
 * the sibling section heading (e.g. "Quick Overview"), leaving both panels'
 * top edges flush. The panel itself is a fixed-height, three-part box: a header
 * that never scrolls, a scrolling list, and a pagination footer. Collapsing
 * hides the list and footer only — the header stays, and the scroll position
 * and page are restored on expand without refetching anything.
 */

export interface ActionPoint {
  key: string;
  label: string;
  count: number;
  /** Exact record ids behind `count` — used to drill down to just those. */
  ids: number[];
  route: string;
  icon: string;
  tone: string;
  urgent?: boolean;
  description?: string;
  /** View permission codes that unlock `route`; holding ANY one is enough. */
  permissions?: string[];
}

/**
 * Tone -> colour classes. Each action point type carries its own tone so the
 * card colour identifies it at a glance, using the existing dashboard palette
 * (flat corners, tinted icon chip, coloured left accent) in light and dark.
 */
const TONES: Record<string, { chip: string; accent: string; count: string }> = {
  amber:   { chip: 'bg-amber-500/10 text-amber-600 dark:text-amber-400',       accent: 'bg-amber-500',   count: 'text-amber-600 dark:text-amber-400' },
  orange:  { chip: 'bg-orange-500/10 text-orange-600 dark:text-orange-400',    accent: 'bg-orange-500',  count: 'text-orange-600 dark:text-orange-400' },
  rose:    { chip: 'bg-rose-500/10 text-rose-600 dark:text-rose-400',          accent: 'bg-rose-500',    count: 'text-rose-600 dark:text-rose-400' },
  indigo:  { chip: 'bg-indigo-500/10 text-indigo-600 dark:text-indigo-400',    accent: 'bg-indigo-500',  count: 'text-indigo-600 dark:text-indigo-400' },
  sky:     { chip: 'bg-sky-500/10 text-sky-600 dark:text-sky-400',             accent: 'bg-sky-500',     count: 'text-sky-600 dark:text-sky-400' },
  emerald: { chip: 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400', accent: 'bg-emerald-500', count: 'text-emerald-600 dark:text-emerald-400' },
  teal:    { chip: 'bg-teal-500/10 text-teal-600 dark:text-teal-400',          accent: 'bg-teal-500',    count: 'text-teal-600 dark:text-teal-400' },
  violet:  { chip: 'bg-violet-500/10 text-violet-600 dark:text-violet-400',    accent: 'bg-violet-500',  count: 'text-violet-600 dark:text-violet-400' },
  fuchsia: { chip: 'bg-fuchsia-500/10 text-fuchsia-600 dark:text-fuchsia-400', accent: 'bg-fuchsia-500', count: 'text-fuchsia-600 dark:text-fuchsia-400' },
  slate:   { chip: 'bg-slate-500/10 text-slate-600 dark:text-slate-400',       accent: 'bg-slate-400',   count: 'text-slate-600 dark:text-slate-300' },
};

const FALLBACK_TONE = TONES.slate;

const PAGE_SIZE_OPTIONS = [5, 10, 20];
const DEFAULT_PAGE_SIZE = 5;

/** Fixed body height — the card keeps the same footprint whatever the page
 *  size or number of points, and the list scrolls inside it. */
const LIST_HEIGHT = 'h-[290px]';

/** Nav button styling, matching DataTablePagination's, sized for this column. */
const NAV_BTN =
  'w-6 h-6 flex items-center justify-center rounded-none border border-slate-200 dark:border-slate-800 ' +
  'bg-white dark:bg-slate-900 hover:bg-slate-50 dark:hover:bg-slate-800 disabled:opacity-40 ' +
  'disabled:hover:bg-white dark:disabled:hover:bg-slate-900 disabled:cursor-not-allowed transition ' +
  'cursor-pointer text-slate-600 dark:text-slate-300';

interface ActionPointsCardProps {
  points: ActionPoint[] | null | undefined;
  /** True while the first fetch is in flight (renders skeleton rows). */
  loading?: boolean;
  title?: string;
  subtitle?: string;
  emptyTitle?: string;
  emptyDescription?: string;
  className?: string;
  /**
   * Render the title/subtitle ABOVE the panel instead of inside its header, so
   * it aligns with the section heading of the column beside it.
   */
  headingOutside?: boolean;
  /** Spacing under the external heading — match the sibling column's. */
  headingClassName?: string;
  /**
   * Stretch the panel to the full height of its grid cell (xl and up), so it
   * ends level with the taller column beside it, and let the list absorb the
   * leftover space instead of using the fixed body height. The list keeps a
   * `min-height: 0` so it never grows the row — the neighbouring column alone
   * decides the height, and anything that doesn't fit scrolls.
   *
   * Below xl the grid is single-column, where there is no neighbour to match:
   * the fixed body height applies as usual. Off by default, so dashboards that
   * size the card naturally are unaffected.
   */
  fillHeight?: boolean;
  pageSizeOptions?: number[];
  defaultPageSize?: number;
}

/** Drill-down URL pinned to the exact ids behind a point's count. */
function drillTo(base: string, ids: number[], label: string) {
  const query = ids.length > 0 ? `?ids=${ids.join(',')}&label=${encodeURIComponent(label)}` : '';
  return `${base}${query}`;
}

export default function ActionPointsCard({
  points,
  loading = false,
  title = 'Action Points',
  subtitle = 'Pending items that need your attention.',
  emptyTitle = "You're all caught up",
  emptyDescription = 'No pending actions right now.',
  className = '',
  headingOutside = false,
  headingClassName = 'mb-3',
  fillHeight = false,
  pageSizeOptions = PAGE_SIZE_OPTIONS,
  defaultPageSize = DEFAULT_PAGE_SIZE,
}: ActionPointsCardProps) {
  const router = useRouter();
  const { user } = useAuth();
  const bodyId = useId();

  const [pageIndex, setPageIndex] = useState(0);
  const [pageSize, setPageSize] = useState(defaultPageSize);

  const listRef = useRef<HTMLDivElement | null>(null);
  const scrollTopRef = useRef(0);

  // Only surface points that actually need attention (count > 0), and re-check
  // the View permission of each one against this user's own permission list.
  const openPoints = useMemo(
    () =>
      (points ?? [])
        .filter((p) => p.count > 0)
        .map((p) => ({ ...p, canView: hasAnyDjangoPermission(user, p.permissions) })),
    [points, user],
  );

  const totalPending = openPoints.reduce((n, p) => n + p.count, 0);

  // Page is derived and clamped on every render, so a background refresh that
  // shortens the list can never leave the view on a page that no longer exists.
  const pageCount = Math.max(1, Math.ceil(openPoints.length / pageSize));
  const page = Math.min(pageIndex, pageCount - 1);
  const firstRow = page * pageSize;
  const pageItems = openPoints.slice(firstRow, firstRow + pageSize);

  /** Move to a page and start it from the top (scroll offset is per-page). */
  const goToPage = (next: number) => {
    setPageIndex(Math.max(0, Math.min(next, pageCount - 1)));
    scrollTopRef.current = 0;
    if (listRef.current) listRef.current.scrollTop = 0;
  };

  const summary =
    totalPending > 0
      ? `${totalPending} item${totalPending === 1 ? '' : 's'} pending`
      : 'No pending items';

  return (
    <div className={`${fillHeight ? 'xl:relative' : ''} ${className}`}>
      {/*
        With `fillHeight`, the contents are taken out of flow at xl and pinned to
        the grid cell. That is what makes the height match: an in-flow column
        contributes its max-content height to the grid row, so a long list would
        drag the row taller than the column beside it. Out of flow it contributes
        nothing, the row is sized by that neighbour alone, and `inset-0` fills
        whatever height results. Below xl the grid is one column with no
        neighbour to match, so everything stays in normal flow.
      */}
      <div className={fillHeight ? 'flex flex-col xl:absolute xl:inset-0' : undefined}>
        {/* External heading — same markup as the sibling section heading, so both
            columns' panels start at exactly the same vertical offset. */}
        {headingOutside && (
          <div className={`${fillHeight ? 'shrink-0' : ''} ${headingClassName}`}>
            <h3 className="text-base font-bold text-[#495057] dark:text-white uppercase tracking-wider">
              {title}
            </h3>
            <p className="text-xs text-vz-muted mt-0.5 truncate">{subtitle}</p>
          </div>
        )}

        <section
          className={`bg-white dark:bg-slate-900 border border-vz-border dark:border-slate-800 shadow-sm flex flex-col ${
            fillHeight ? 'xl:flex-1 xl:min-h-0' : ''
          }`}
          aria-label={title}
        >
          {/* Header — always visible, no collapse functionality */}
          <div className="shrink-0 px-4 py-3 border-b border-vz-border dark:border-slate-800 flex items-start justify-between gap-2">
            <div className="min-w-0">
              {headingOutside ? (
                <p className="text-[11px] font-bold uppercase tracking-wider text-vz-muted flex items-center gap-2">
                  <i className="fa-solid fa-list-check text-[#405189] dark:text-indigo-400" />
                  {summary}
                </p>
              ) : (
                <>
                  <h3 className="text-sm font-bold text-[#495057] dark:text-white uppercase tracking-wider flex items-center gap-2">
                    <i className="fa-solid fa-list-check text-[#405189] dark:text-indigo-400" />
                    {title}
                  </h3>
                  <p className="text-[10px] text-vz-muted mt-0.5 leading-snug">{subtitle}</p>
                </>
              )}
            </div>

            <div className="shrink-0 flex items-center gap-1.5">
              {totalPending > 0 && (
                <span className="text-[10px] font-black bg-[#405189] text-white px-2 py-1 leading-none">
                  {totalPending}
                </span>
              )}
            </div>
          </div>

          {/* Body — always visible */}
          <div id={bodyId} className={fillHeight ? 'flex flex-col xl:flex-1 xl:min-h-0' : ''}>
            {/* The only scrolling region — the header above and the pagination
                footer below stay put. With `fillHeight` it takes whatever height
                the card has left over (min-h-0 keeps it from growing the row);
                otherwise it uses the fixed body height. */}
            <div
              ref={listRef}
              onScroll={(e) => {
                scrollTopRef.current = (e.target as HTMLDivElement).scrollTop;
              }}
              className={`${LIST_HEIGHT} ${
                fillHeight ? 'xl:h-auto xl:flex-1 xl:min-h-0' : ''
              } overflow-y-auto custom-scrollbar`}
            >
              {loading && points == null ? (
                // Skeleton only on the very first load (nothing fetched yet), so
                // a background refresh never blanks an already-painted list.
                <div className="p-3 space-y-2">
                  {[0, 1, 2, 3].map((i) => (
                    <div key={i} className="h-12 bg-slate-100 dark:bg-slate-800 animate-pulse" />
                  ))}
                </div>
              ) : openPoints.length === 0 ? (
                <div className="h-full flex flex-col items-center justify-center text-center px-4">
                  <span className="w-10 h-10 mb-2 rounded-full bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 flex items-center justify-center">
                    <i className="fa-solid fa-circle-check" />
                  </span>
                  <p className="text-sm font-bold text-[#495057] dark:text-white">{emptyTitle}</p>
                  <p className="text-xs text-vz-muted mt-0.5">{emptyDescription}</p>
                </div>
              ) : (
                <ul className="divide-y divide-slate-100 dark:divide-slate-800">
                  {pageItems.map((p) => {
                    const tone = TONES[p.tone] ?? FALLBACK_TONE;
                    const rowTitle = p.canView
                      ? `Show these ${p.count} record(s)`
                      : 'You do not have permission to view this module';

                    return (
                      <li key={p.key}>
                        <button
                          type="button"
                          disabled={!p.canView}
                          aria-disabled={!p.canView}
                          onClick={() => router.push(drillTo(p.route, p.ids, p.label))}
                          title={rowTitle}
                          className={`w-full text-left flex items-stretch gap-0 transition duration-200 ${
                            p.canView
                              ? 'hover:bg-slate-50 dark:hover:bg-slate-800/50 cursor-pointer'
                              : 'opacity-50 cursor-not-allowed'
                          }`}
                        >
                          {/* Colour accent identifies the action point type */}
                          <span className={`w-1 shrink-0 ${tone.accent}`} aria-hidden="true" />
                          <span className="flex-1 min-w-0 flex items-center gap-3 px-3 py-2.5">
                            <span className={`w-8 h-8 rounded-full flex items-center justify-center text-xs shrink-0 ${tone.chip}`}>
                              <i className={p.icon} />
                            </span>
                            <span className="min-w-0 flex-1">
                              <span className="block text-[11px] font-bold text-[#495057] dark:text-slate-100 leading-snug">
                                {p.label}
                              </span>
                              {p.description && (
                                <span className="block text-[10px] text-vz-muted mt-0.5 leading-snug truncate">
                                  {p.description}
                                </span>
                              )}
                            </span>
                            <span className="shrink-0 flex items-center gap-1.5">
                              {p.urgent && (
                                <span className="text-[8px] font-black uppercase tracking-wider bg-rose-500 text-white px-1.5 py-0.5">
                                  Urgent
                                </span>
                              )}
                              <span className={`text-lg font-black leading-none ${tone.count}`}>{p.count}</span>
                              {p.canView ? (
                                <i className="fa-solid fa-chevron-right text-[9px] text-slate-300 dark:text-slate-600" />
                              ) : (
                                <i className="fa-solid fa-lock text-[9px] text-slate-300 dark:text-slate-600" />
                              )}
                            </span>
                          </span>
                        </button>
                      </li>
                    );
                  })}
                </ul>
              )}
            </div>

            {/* Pagination — same controls and wording as DataTablePagination,
                condensed for this column's width. */}
            {openPoints.length > 0 && (
              <div className="shrink-0 flex flex-wrap items-center justify-between gap-x-3 gap-y-2 px-3 py-2 border-t border-vz-border dark:border-slate-800 bg-slate-50/50 dark:bg-slate-950/20 text-[10px] font-semibold text-slate-500 dark:text-slate-400">
                <span className="text-slate-400 dark:text-slate-500">
                  <span className="text-slate-700 dark:text-slate-300">{firstRow + 1}</span>–
                  <span className="text-slate-700 dark:text-slate-300">
                    {Math.min(firstRow + pageSize, openPoints.length)}
                  </span>{' '}
                  of <span className="text-slate-700 dark:text-slate-300">{openPoints.length}</span>
                </span>

                <div className="flex items-center gap-1.5">
                  <SimpleSelect
                    value={pageSize}
                    onChange={(val) => {
                      setPageSize(Number(val));
                      goToPage(0);
                    }}
                    className="w-[58px] font-extrabold"
                    isClearable={false}
                    options={pageSizeOptions.map((size) => ({ value: size, label: String(size) }))}
                  />

                  <div className="flex items-center gap-0.5">
                    <button
                      type="button"
                      onClick={() => goToPage(0)}
                      disabled={page === 0}
                      className={NAV_BTN}
                      title="First Page"
                    >
                      <i className="fa-solid fa-angles-left text-[9px]" />
                    </button>
                    <button
                      type="button"
                      onClick={() => goToPage(page - 1)}
                      disabled={page === 0}
                      className={NAV_BTN}
                      title="Previous Page"
                    >
                      <i className="fa-solid fa-angle-left text-[9px]" />
                    </button>
                    <span className="px-1 select-none whitespace-nowrap">
                      <strong className="text-slate-800 dark:text-slate-200 font-extrabold">{page + 1}</strong>
                      {' / '}
                      <strong className="text-slate-800 dark:text-slate-200 font-extrabold">{pageCount}</strong>
                    </span>
                    <button
                      type="button"
                      onClick={() => goToPage(page + 1)}
                      disabled={page >= pageCount - 1}
                      className={NAV_BTN}
                      title="Next Page"
                    >
                      <i className="fa-solid fa-angle-right text-[9px]" />
                    </button>
                    <button
                      type="button"
                      onClick={() => goToPage(pageCount - 1)}
                      disabled={page >= pageCount - 1}
                      className={NAV_BTN}
                      title="Last Page"
                    >
                      <i className="fa-solid fa-angles-right text-[9px]" />
                    </button>
                  </div>
                </div>
              </div>
            )}
          </div>
        </section>
      </div>
    </div>
  );
}
