import type { Meta, StoryObj } from '../../story.types';
import type { Option } from '../types';
import { AsyncSearchSelect } from './async-search-select.component';

type Story = StoryObj<AsyncSearchSelect>;

const fewOptions: Option[] = [
  { value: 'c1', label: 'Acme Corporation' },
  { value: 'c2', label: 'Globex Industries' },
  { value: 'c3', label: 'Initech Technologies' },
  { value: 'c4', label: 'Umbrella Labs' },
];

const longOptions: Option[] = Array.from({ length: 50 }, (_, i) => ({
  value: `u${i + 1}`,
  label: `User ${String(i + 1).padStart(3, '0')}`,
}));

const meta: Meta<AsyncSearchSelect> = {
  title: '@aurora/components/async-search-select',
  component: AsyncSearchSelect,
  tags: ['autodocs'],
  argTypes: {
    value: {
      control: 'text',
      description:
        'Currently selected option value (id). `null` means no selection. Host is responsible for keeping `value` and `options` consistent — if the selected value is not present in `options`, the trigger shows empty.',
    },
    options: {
      control: 'object',
      description:
        'Flat array of `Option` to display. The host typically loads this in response to `searchChange` (async paginated query). Internal client-side filtering is disabled — whatever comes in `options` is shown.',
    },
    loading: {
      control: 'boolean',
      description: 'Shows a spinner inside the popover while options are being fetched.',
    },
    placeholder: {
      control: 'text',
      description: 'Placeholder shown inside the combobox input when no selection exists.',
    },
    disabled: {
      control: 'boolean',
      description: 'Disables the combobox trigger and prevents opening the popover.',
    },
    emptyMessage: {
      control: 'text',
      description:
        'Message shown inside the popover when `options` is empty and `loading` is false. Should be already translated by the host.',
    },
    errorMessage: {
      control: 'text',
      description:
        'Optional error message rendered below the combobox. `null` means no error. Host passes the already-translated error string (e.g. from a FormControl).',
    },
    debounce: {
      control: { type: 'number', min: 0, step: 100 },
      description:
        'Milliseconds to debounce `searchChange` emissions. The combobox emits every keystroke internally; this component debounces before bubbling up.',
    },
    valueChange: {
      action: 'valueChange',
      description:
        'Emits the newly selected `Option.value` (string id) or `null` when the selection is cleared.',
    },
    searchChange: {
      action: 'searchChange',
      description:
        'Emits the debounced search string every time the user pauses typing. Host reacts by loading a new page of `options` filtered server-side.',
    },
  },
  args: {
    value: null,
    options: fewOptions,
    loading: false,
    placeholder: 'Select client...',
    disabled: false,
    emptyMessage: 'No clients found',
    errorMessage: null,
    debounce: 300,
  },
  parameters: {
    aurora: {
      category: 'form',
      when: 'Many-to-one relationship picker with server-side search over a large catalog (>500 options). Runtime target of the YAML `widget.type: async-search-select`. For multi-select, see `<au-async-multiple-search-select>`.',
      whenNot:
        'For many-to-one with a small set (<20) use `<hlm-select>`. For 20–500 options preloaded in memory use `<au-search-select>` (sync filter, no server roundtrip). For many-to-many, use the multi variants.',
    },
  },
};

export default meta;

export const Default: Story = {};

export const Loading: Story = {
  args: {
    loading: true,
    options: [],
    placeholder: 'Loading clients...',
  },
  parameters: {
    docs: {
      description: {
        story:
          'Spinner inside the popover while the host is fetching options. Typical during first open or after a `searchChange` while waiting for the server to respond.',
      },
    },
  },
};

export const Empty: Story = {
  args: {
    options: [],
    loading: false,
    emptyMessage: 'No results matched your search',
  },
  parameters: {
    docs: {
      description: {
        story:
          'Server returned an empty result set (or the initial query has no data yet). The host controls the message text.',
      },
    },
  },
};

export const WithPreselected: Story = {
  name: 'With preselected value',
  args: {
    value: 'c2',
    options: fewOptions,
  },
  parameters: {
    docs: {
      description: {
        story:
          'Component initializes with a value already set (typical edit mode). The trigger shows the label of the pre-selected option via `itemToString`. The host must include the selected option in the `options` array on first load — otherwise the trigger appears empty until the user interacts.',
      },
    },
  },
};

export const LongList: Story = {
  name: 'Long option list (scrollable)',
  args: {
    options: longOptions,
    placeholder: 'Search user...',
    emptyMessage: 'No users found',
  },
  parameters: {
    docs: {
      description: {
        story:
          'Fifty options to validate that the popover scrolls and keyboard navigation (Arrow Up/Down, Home/End) behaves correctly inside `hlm-combobox-list`. In production the host rarely passes 50 items at once — it would paginate via `searchChange`.',
      },
    },
  },
};

export const Disabled: Story = {
  args: {
    disabled: true,
    placeholder: 'Disabled',
    options: fewOptions,
  },
  parameters: {
    docs: {
      description: {
        story: 'Disabled trigger blocks the popover from opening and dims the control.',
      },
    },
  },
};

export const Error: Story = {
  name: 'With error message',
  args: {
    value: null,
    errorMessage: 'Select a client to continue',
  },
  parameters: {
    docs: {
      description: {
        story:
          'Error message rendered below the combobox. The host passes the already-translated error string; this component does not reach into the parent form to read `FormControl.errors` — matches the "pure input/output" contract of the library.',
      },
    },
  },
};
