'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 {StoreCard} from './StoreCard';
import type {Store, StoreStatus} from '@/features/stores/types/store';
import type {Resource} from '@/shared/hooks/useResource';

type Filter = 'all' | StoreStatus;

/**
 * Three-up on desktop, per spec.
 *
 * minWidth 270 is what actually yields three tracks once the 16px gaps are
 * subtracted from a ~880px workspace — 300 silently collapsed it to two.
 * Capped at 3 so a wide monitor doesn't stretch the roster into a thin strip,
 * and it steps down to two when the Loyaly AI panel is taking 380px.
 */
const STORE_GRID_COLUMNS = {minWidth: 270, max: 3, repeat: 'fit'} as const;

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

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

  // Counts come from the unfiltered set so the chips still read "Closed (1)"
  // while the closed filter is active — otherwise every chip but the selected
  // one shows zero, which is useless.
  const counts = useMemo(() => {
    const c: Record<Filter, number> = {
      all: stores.length,
      open: 0,
      maintenance: 0,
      closed: 0,
    };
    for (const s of stores) c[s.status] += 1;
    return c;
  }, [stores]);

  const visible = useMemo(() => {
    const q = query.trim().toLowerCase();
    return stores.filter(
      (s) =>
        (filter === 'all' || s.status === filter) &&
        (q === '' || s.name.toLowerCase().includes(q)),
    );
  }, [stores, query, filter]);

  return (
    <CollectionRegion label="All stores">
      <FilterBar
        query={query}
        onQueryChange={setQuery}
        placeholder="Search stores…"
        filterValue={filter}
        onFilterChange={(v) => setFilter(v as Filter)}
        filterLabel="Filter stores by status"
        options={[
          {value: 'all', label: 'All', hint: counts.all},
          {value: 'open', label: 'Open', hint: counts.open},
          {value: 'maintenance', label: 'Maintenance', hint: counts.maintenance},
          {value: 'closed', label: 'Closed', hint: counts.closed},
        ]}
        resultCount={visible.length}
        resultNoun={visible.length === 1 ? 'store' : 'stores'}
      />

      <AsyncBoundary
        resource={resource}
        loading={<SkeletonCardGrid count={6} height={240} />}
        empty={
          <EmptyPanel
            icon="stores"
            title="No stores yet"
            description="Stores appear here once they are added to your Loyaly account."
          />
        }
      >
        {() =>
          visible.length === 0 ? (
            // A filter returning nothing is a different situation from having
            // no stores at all, and needs a different way out.
            <EmptyPanel
              icon="stores"
              title="No stores match"
              description="Try a different status filter or clear the search."
            />
          ) : (
            <Grid columns={STORE_GRID_COLUMNS} gap={4}>
              {visible.map((s) => (
                <StoreCard key={s.id} store={s} />
              ))}
            </Grid>
          )
        }
      </AsyncBoundary>
    </CollectionRegion>
  );
}
