import { observer } from 'mobx-react-lite';
import { useEffect, useState } from 'react';
import type { GridListItemProps, GridListProps } from 'react-aria-components';
import { GridList, GridListItem } from 'react-aria-components';
import { type Selection } from 'react-stately';
import { twMerge } from 'tailwind-merge';

import { useStores } from '@/app/(root)/AppProviders';
import MultiSelectContext, {
  MultiSelectContextProvider,
} from '@/components/clipBrowser/MultiSelectContext';
import { usePreviewContext } from '@/context/PreviewContext';
import { useBreakpointMd } from '@/hooks/useBreakpoint';
import { useContextSelector } from '@/hooks/useContextSelector';
import { Clip } from '@/state/clipStore';

// Internal component that uses MultiSelectContext
const GridListWrapperInternal = observer(
  <T extends object>({
    children,
    ...props
  }: GridListProps<T> & {
    // Callback invoked when selection changes. Provides:
    // - selection: The current Selection state (synchronous, from React Aria)
    // - setPreviewClip: Function to update preview context
    afterSelectionChange?: (props: {
      setPreviewClip?: (clip: Clip) => void;
      selection: Selection;
    }) => void;
  }) => {
    // Sync to MultiSelectContext
    const setMultiSelectClipIds = useContextSelector(
      MultiSelectContext,
      (ctx) => ctx.setSelectedClipIds
    );

    const clearMultiSelect = useContextSelector(
      MultiSelectContext,
      (ctx) => ctx.clear
    );

    // NOTE: We need to keep this selected state variable here so that Shift-multi-select works correctly
    const [selected, setSelected] = useState<Selection>(new Set([]));
    const { setPreviewClip } = usePreviewContext();
    const { menus, clips, genForm } = useStores();
    const { afterSelectionChange } = props;
    const isMobile = !useBreakpointMd();

    useEffect(() => {
      // Sync external selection (menus.selected) to local state when a single item is selected
      // This ensures the GridList visual state stays in sync with external selection changes
      if (
        [...menus.selected].length === 1 &&
        selected !== 'all' &&
        !selected.has([...menus.selected][0])
      ) {
        setSelected(new Set([...menus.selected]));
        // Notify parent with the synchronized selection state
        afterSelectionChange?.({
          setPreviewClip,
          selection: new Set([...menus.selected]),
        });
      }
    }, [menus.selected]);

    return (
      <GridList
        selectionMode={isMobile ? 'single' : 'multiple'}
        selectionBehavior={'replace'}
        selectedKeys={selected}
        onSelectionChange={(selection: Selection) => {
          if (new Set(selection).size === 1) {
            const sClip = clips.clips.filter(
              (clip) => clip.id === [...selection][0]
            )?.[0];
            if (sClip) {
              genForm.setSelectedClip(sClip);
              setPreviewClip(sClip);
            }
          }
          setSelected(selection);

          // Sync to MultiSelectContext (which will update menus.setSelected asynchronously)
          if (selection !== 'all' && new Set(selection).size > 1) {
            const selectedIds = [...selection].map(String);
            setMultiSelectClipIds(selectedIds);
          } else {
            clearMultiSelect();
          }

          // Pass synchronous selection state to parent callback
          // This allows parent to use fresh selection data instead of waiting for async MobX updates
          afterSelectionChange?.({ setPreviewClip, selection });
        }}
        {...props}
      >
        {children}
      </GridList>
    );
  }
);

// Wrapper that provides MultiSelectContext to all GridListWrapper usages
export const GridListWrapper = <T extends object>(
  props: GridListProps<T> & {
    afterSelectionChange?: (props: {
      setPreviewClip?: (clip: Clip) => void;
      selection: Selection;
    }) => void;
  }
) => {
  return (
    <MultiSelectContextProvider>
      <GridListWrapperInternal {...props} />
    </MultiSelectContextProvider>
  );
};

export function GridListItemWrapper({ children, ...props }: GridListItemProps) {
  const className = props.className;
  return (
    <GridListItem
      {...props}
      // need to do this because className can be a function from react-aria-components
      className={
        typeof className === 'function'
          ? (renderProps: Parameters<typeof className>[0]) =>
              twMerge('group', className(renderProps))
          : twMerge('group', className || '')
      }
    >
      {children}
    </GridListItem>
  );
}
