'use client';

import {useMemo, useState} from 'react';
import {Grid} from '@astryxdesign/core/Grid';
import {AsyncBoundary} from '@/shared/components/data/AsyncBoundary';
import {FilterBar} from '@/shared/components/patterns/FilterBar';
import {CollectionRegion} from '@/shared/components/patterns/CollectionRegion';
import {EmptyPanel} from '@/shared/components/patterns/EmptyPanel';
import {SkeletonCardGrid} from '@/shared/components/patterns/LoadingState';
import {StaffCard} from './StaffCard';
import {ATTENDANCE} from '@/features/staff/utils/attendance';
import {
  countByAttendance,
  selectStaff,
} from '@/features/staff/services/staffService';
import type {StaffFilter} from '@/features/staff/services/staffService';
import type {StaffMember} from '@/features/staff/types/staff';
import type {Resource} from '@/shared/hooks/useResource';

const STAFF_GRID_COLUMNS = {minWidth: 300, max: 3, repeat: 'fit'} as const;

export function StaffGrid({resource}: {resource: Resource<StaffMember[]>}) {
  const [query, setQuery] = useState('');
  const [filter, setFilter] = useState<StaffFilter>('all');

  // `?? []` allocates a fresh array every render, which would make it an
  // unstable dependency and recompute both memos on every keystroke.
  const staff = useMemo(() => resource.data ?? [], [resource.data]);

  // Both derivations live in staffService: what counts as a match and what
  // order the roster is in are product rules, and a redesign of this grid must
  // not be able to change them by accident. The memos stay here because
  // memoisation is a React concern, not a domain one.
  const counts = useMemo(() => countByAttendance(staff), [staff]);

  const visible = useMemo(
    () => selectStaff(staff, {filter, search: query}),
    [staff, query, filter],
  );

  return (
    <CollectionRegion label="Team roster">
      <FilterBar
        query={query}
        onQueryChange={setQuery}
        placeholder="Search name or role…"
        filterValue={filter}
        onFilterChange={(v) => setFilter(v as StaffFilter)}
        filterLabel="Filter staff by attendance"
        options={[
          {value: 'all', label: 'All', hint: counts.all},
          {value: 'present', label: ATTENDANCE.present.label, hint: counts.present},
          {value: 'late', label: ATTENDANCE.late.label, hint: counts.late},
          {value: 'absent', label: ATTENDANCE.absent.label, hint: counts.absent},
          {value: 'leave', label: ATTENDANCE.leave.label, hint: counts.leave},
        ]}
        resultCount={visible.length}
        resultNoun={visible.length === 1 ? 'person' : 'people'}
      />

      <AsyncBoundary
        resource={resource}
        loading={<SkeletonCardGrid count={6} height={230} minWidth={300} />}
        empty={
          <EmptyPanel
            icon="staff"
            title="No staff yet"
            description="Team members appear here once they are added to a store."
          />
        }
      >
        {() =>
          visible.length === 0 ? (
            <EmptyPanel
              icon="staff"
              title="Nobody matches"
              description="Try a different attendance filter or clear the search."
            />
          ) : (
            <Grid columns={STAFF_GRID_COLUMNS} gap={4}>
              {visible.map((m) => (
                <StaffCard key={m.id} member={m} />
              ))}
            </Grid>
          )
        }
      </AsyncBoundary>
    </CollectionRegion>
  );
}
