import type { Meta, StoryObj } from '@storybook/react'
import { useState, type ComponentProps, type ComponentType } from 'react'
import { expect } from 'storybook/test'
import { Svg3DPerspectiveGrid } from '@chainlink/blocks-icons'
import {
  Combobox,
  ComboboxBadge,
  ComboboxClear,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxGroup,
  ComboboxInput,
  ComboboxItem,
  ComboboxLabel,
  ComboboxList,
  ComboboxSelectAll,
  ComboboxSeparator,
  ComboboxTagsValue,
  ComboboxTrigger,
  ComboboxValue,
} from './Combobox'

type ComboboxStoryProps = Omit<ComponentProps<typeof Combobox>, 'children'>

type DemoOption = {
  value: string
  label: string
  src: string
}

type NetworkOption = {
  value: string
  label: string
}

const demoOptions: DemoOption[] = [
  {
    value: 'arbitrum',
    label: 'Arbitrum',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/arbitrum.svg?auto=compress%2Cformat',
  },
  {
    value: 'avalanche',
    label: 'Avalanche',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/avalanche.svg?auto=compress%2Cformat',
  },
  {
    value: 'base',
    label: 'Base',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/base.svg?auto=compress%2Cformat',
  },
  {
    value: 'ethereum',
    label: 'Ethereum',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/ethereum.svg?auto=compress%2Cformat',
  },
  {
    value: 'solana',
    label: 'Solana',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/solana.svg?auto=compress%2Cformat',
  },
  {
    value: 'polygon',
    label: 'Polygon',
    src: 'https://d2f70xi62kby8n.cloudfront.net/bridge/icons/networks/polygon.svg?auto=compress%2Cformat',
  },
]

const meta = {
  component: Combobox as ComponentType<ComboboxStoryProps>,
  title: 'Form/Combobox',
  subcomponents: {
    ComboboxTrigger,
    ComboboxValue,
    ComboboxTagsValue,
    ComboboxClear,
    ComboboxContent,
    ComboboxInput,
    ComboboxList,
    ComboboxItem,
    ComboboxGroup,
    ComboboxLabel,
    ComboboxEmpty,
    ComboboxSelectAll,
    ComboboxSeparator,
    ComboboxBadge,
  },
  argTypes: {
    disabled: {
      control: 'boolean',
      table: {
        type: { summary: 'boolean' },
        defaultValue: { summary: 'false' },
      },
    },
    clearable: {
      control: 'boolean',
      table: {
        type: { summary: 'boolean' },
        defaultValue: { summary: 'true' },
      },
    },
  },
  args: {
    disabled: false,
    clearable: true,
  },
} satisfies Meta<ComboboxStoryProps>

export default meta

type Story = StoryObj<ComboboxStoryProps>

export const Default: Story = {
  render: (_args) => {
    const options = [
      { value: 'arbitrum', label: 'Arbitrum' },
      { value: 'avalanche', label: 'Avalanche' },
      { value: 'base', label: 'Base' },
      { value: 'ethereum', label: 'Ethereum' },
    ]

    return (
      <Combobox
        clearable
        disabled={false}
        items={options}
        itemToStringValue={(opt) => opt.label}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const NotClearable: Story = {
  parameters: {
    docs: {
      description: {
        story:
          'Use `clearable={false}` when a selected value is required or should only change through another selection.',
      },
    },
  },
  render: (_args) => {
    const options = [
      { value: 'arbitrum', label: 'Arbitrum' },
      { value: 'avalanche', label: 'Avalanche' },
      { value: 'base', label: 'Base' },
      { value: 'ethereum', label: 'Ethereum' },
    ]

    return (
      <Combobox
        clearable={false}
        defaultValue={options[0]}
        disabled={false}
        items={options}
        itemToStringValue={(opt) => opt.label}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
  play: async ({ canvasElement }) => {
    const trigger = canvasElement.querySelector(
      '[data-slot="combobox-trigger"]',
    ) as HTMLElement
    await expect(
      trigger.querySelector('[data-slot="combobox-clear"]'),
    ).toBeNull()
  },
}

export const SmallSize: Story = {
  render: (_args) => {
    const options = [
      { value: 'arbitrum', label: 'Arbitrum' },
      { value: 'avalanche', label: 'Avalanche' },
      { value: 'base', label: 'Base' },
      { value: 'ethereum', label: 'Ethereum' },
    ]

    return (
      <Combobox
        clearable
        disabled={false}
        items={options}
        itemToStringValue={(opt) => opt.label}
        size="sm"
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const OpenState: Story = {
  render: (args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <Combobox
        items={[
          { value: 'arbitrum', label: 'Arbitrum' },
          { value: 'avalanche', label: 'Avalanche' },
          { value: 'base', label: 'Base' },
          { value: 'ethereum', label: 'Ethereum' },
        ]}
        itemToStringValue={(opt: NetworkOption) => opt.label}
        {...openProp}
        {...args}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: NetworkOption) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const XsSize: Story = {
  render: (args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        {...openProp}
        {...args}
        size="xs"
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const WithIcons: Story = {
  render: (args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        {...openProp}
        {...args}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network...">
            {(option: DemoOption) => (
              <>
                <Svg3DPerspectiveGrid />
                {option.label}
              </>
            )}
          </ComboboxValue>
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                <Svg3DPerspectiveGrid />
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const WithImages: Story = {
  render: (args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        {...openProp}
        {...args}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network...">
            {(option: DemoOption) => (
              <>
                <img
                  className="h-6 w-6"
                  width="24"
                  height="24"
                  src={option.src}
                  alt={option.label}
                />
                {option.label}
              </>
            )}
          </ComboboxValue>
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                <img
                  className="h-6 w-6"
                  width="24"
                  height="24"
                  src={option.src}
                  alt={option.label}
                />
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const ClearButton: Story = {
  render: (args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        defaultValue={demoOptions[3]}
        {...openProp}
        {...args}
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network...">
            {(option: DemoOption) => (
              <>
                <img
                  className="h-6 w-6"
                  width="24"
                  height="24"
                  src={option.src}
                  alt={option.label}
                />
                {option.label}
              </>
            )}
          </ComboboxValue>
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                <img
                  className="h-6 w-6"
                  width="24"
                  height="24"
                  src={option.src}
                  alt={option.label}
                />
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    )
  },
}

export const EmptyStates: Story = {
  parameters: {
    docs: {
      description: {
        story:
          'Shows the empty result message when filtering removes every item from the list.',
      },
    },
  },
  render: (_args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <div className="flex items-start gap-8">
        <Combobox
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
        >
          <ComboboxTrigger>
            <ComboboxValue placeholder="Default" />
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>

        <Combobox
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
          size="sm"
        >
          <ComboboxTrigger className="w-56">
            <ComboboxValue placeholder="Small" />
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>

        <Combobox
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
          size="xs"
        >
          <ComboboxTrigger className="w-56">
            <ComboboxValue placeholder="Extra small" />
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>
      </div>
    )
  },
}

export const EmptyStatesMulti: Story = {
  parameters: {
    docs: {
      description: {
        story:
          'Shows the empty result message for multi-select comboboxes across available sizes.',
      },
    },
  },
  render: (_args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return (
      <div className="flex items-start gap-8">
        <Combobox
          multiple
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
        >
          <ComboboxTrigger className="w-56">
            <ComboboxTagsValue<DemoOption> placeholder="Default">
              {(opt: DemoOption) => opt.label}
            </ComboboxTagsValue>
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>

        <Combobox
          multiple
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
          size="sm"
        >
          <ComboboxTrigger className="w-56">
            <ComboboxTagsValue<DemoOption> placeholder="Small">
              {(opt: DemoOption) => opt.label}
            </ComboboxTagsValue>
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>

        <Combobox
          multiple
          items={demoOptions}
          itemToStringValue={(opt: DemoOption) => opt.label}
          defaultInputValue="zzz"
          {...openProp}
          size="xs"
        >
          <ComboboxTrigger className="w-56">
            <ComboboxTagsValue<DemoOption> placeholder="Extra small">
              {(opt: DemoOption) => opt.label}
            </ComboboxTagsValue>
          </ComboboxTrigger>
          <ComboboxContent>
            <ComboboxInput placeholder="Search..." />
            <ComboboxEmpty>No results found.</ComboboxEmpty>
            <ComboboxList>
              {(option: DemoOption) => (
                <ComboboxItem key={option.value} value={option}>
                  {option.label}
                </ComboboxItem>
              )}
            </ComboboxList>
          </ComboboxContent>
        </Combobox>
      </div>
    )
  },
}

export const Disabled: Story = {
  render: (args) => (
    <div className="flex items-start gap-8">
      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        {...args}
        disabled
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>

      <Combobox
        items={demoOptions}
        itemToStringValue={(opt: DemoOption) => opt.label}
        defaultValue={demoOptions[3]}
        {...args}
        disabled
      >
        <ComboboxTrigger>
          <ComboboxValue placeholder="Select network..." />
        </ComboboxTrigger>
        <ComboboxContent>
          <ComboboxInput placeholder="Search..." />
          <ComboboxEmpty>No results found.</ComboboxEmpty>
          <ComboboxList>
            {(option: DemoOption) => (
              <ComboboxItem key={option.value} value={option}>
                {option.label}
              </ComboboxItem>
            )}
          </ComboboxList>
        </ComboboxContent>
      </Combobox>
    </div>
  ),
}

function MultiSelectDemo({
  openProp = {},
  maxCount = 3,
}: {
  openProp?: { defaultOpen?: boolean }
  maxCount?: number
}) {
  const [selected, setSelected] = useState<DemoOption[]>([
    demoOptions[0],
    demoOptions[3],
    demoOptions[4],
    demoOptions[5],
  ])

  return (
    <Combobox
      multiple
      items={demoOptions}
      itemToStringValue={(opt: DemoOption) => opt.label}
      value={selected}
      onValueChange={(v: unknown) => setSelected((v ?? []) as DemoOption[])}
      {...openProp}
    >
      <ComboboxTrigger className="w-[300px]">
        <ComboboxTagsValue<DemoOption>
          placeholder="Select networks..."
          maxCount={maxCount}
        >
          {(opt: DemoOption) => (
            <>
              <img
                className="size-4"
                width="16"
                height="16"
                src={opt.src}
                alt={opt.label}
              />
              {opt.label}
            </>
          )}
        </ComboboxTagsValue>
      </ComboboxTrigger>
      <ComboboxContent>
        <ComboboxInput placeholder="Search..." />
        <ComboboxEmpty>No results found.</ComboboxEmpty>
        <ComboboxList>
          {(option: DemoOption) => (
            <ComboboxItem key={option.value} value={option}>
              <img
                className="size-6"
                width="24"
                height="24"
                src={option.src}
                alt={option.label}
              />
              {option.label}
            </ComboboxItem>
          )}
        </ComboboxList>
      </ComboboxContent>
    </Combobox>
  )
}

export const MultiSelect: Story = {
  parameters: {
    docs: {
      description: {
        story:
          'Use multi-select when users can choose several values from the same option set.',
      },
    },
  },
  render: (_args, context) => {
    const openProp = context.viewMode === 'story' ? { defaultOpen: true } : {}

    return <MultiSelectDemo openProp={openProp} />
  },
}
