import type { Meta, StoryObj } from '@storybook/nextjs-vite'

import { expect } from 'storybook/test'

import { Input } from './Input'
import { TextField } from 'react-aria-components'
import { MaterialIcon } from '../Icons/MaterialIcon'

const meta: Meta<typeof Input> = {
  title: 'Core Components/Input (New)',
  // @ts-expect-error Input does not support this, but wrapping <TextField> does
  component: ({ isInvalid, validationState, ...props }) => (
    <TextField isInvalid={isInvalid} data-validation-state={validationState}>
      <Input {...props} data-validation-state={validationState} />
    </TextField>
  ),
  argTypes: {
    label: {
      control: 'text',
      description: 'The label text displayed for the input field',
      table: {
        type: { summary: 'string' },
      },
    },
    labelPosition: {
      control: 'select',
      options: ['floating', 'top'],
      description: 'Position of the label relative to the input',
      table: {
        type: { summary: "'floating' | 'top'" },
        defaultValue: { summary: "'floating'" },
      },
    },
    placeholder: {
      control: 'text',
      description: 'Placeholder text shown when input is empty',
      table: {
        type: { summary: 'string' },
        defaultValue: { summary: 'undefined' },
      },
    },
    required: {
      control: 'boolean',
      description: 'Whether the input is required',
      table: {
        type: { summary: 'boolean' },
        defaultValue: { summary: 'false' },
      },
    },
    disabled: {
      control: 'boolean',
      description: 'Whether the input is disabled',
      table: {
        type: { summary: 'boolean' },
        defaultValue: { summary: 'false' },
      },
    },
    showClearContentIcon: {
      control: 'boolean',
      description: 'Whether the clear content icon is shown',
      table: {
        type: { summary: 'boolean' },
        defaultValue: { summary: 'false' },
      },
    },
  },
}

export default meta

type Story = StoryObj<typeof Input>

export const Default: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    required: false,
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).not.toBeDisabled()

    expect(textbox).toHaveValue('')

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('Hello World')

    await userEvent.clear(textbox)
    expect(textbox).toHaveValue('')
  },
}

export const WithIconsFloatingLabel: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    value: 'Value',
    leftIcon: <MaterialIcon icon="sell" />,
    rightIcon: <MaterialIcon icon="lock" />,
    showClearContentIcon: true,
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')

    expect(textbox).not.toBeDisabled()
  },
}

export const WithIconsTopLabel: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    value: 'Value',
    labelPosition: 'top',
    leftIcon: <MaterialIcon icon="email" />,
    showClearContentIcon: true,
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')

    expect(textbox).not.toBeDisabled()
  },
}

export const WithIconsAndClearIconTopLabel: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    value: 'Value',
    labelPosition: 'top',
    leftIcon: <MaterialIcon icon="person" />,
    rightIcon: <MaterialIcon icon="email" />,
    showClearContentIcon: true,
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')

    expect(textbox).not.toBeDisabled()
  },
}

export const Filled: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    value: 'Value',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')

    expect(textbox).not.toBeDisabled()
  },
}

export const Error: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    isInvalid: true,
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveAttribute('aria-invalid', 'true')
    expect(textbox).not.toBeDisabled()
  },
}

export const ErrorFilled: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    isInvalid: true,
    value: 'Value',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    expect(textbox).toHaveAttribute('aria-invalid', 'true')
    expect(textbox).not.toBeDisabled()
  },
}

export const Disabled: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    disabled: true,
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('')
    expect(textbox).toBeDisabled()

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('')
  },
}

export const DisabledFilled: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    disabled: true,
    value: 'Value',
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    expect(textbox).toBeDisabled()

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('Value')
  },
}

export const WarningDefault: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    validationState: 'warning',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    // data-validation-state is on the parent label element, not the input
    const container = textbox.closest('[data-validation-state]')
    expect(container?.getAttribute('data-validation-state')).toBe('warning')
    expect(textbox).not.toBeDisabled()
  },
}

export const WarningFilled: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    validationState: 'warning',
    value: 'Value',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    // data-validation-state is on the parent label element, not the input
    const container = textbox.closest('[data-validation-state]')
    expect(container?.getAttribute('data-validation-state')).toBe('warning')
    expect(textbox).not.toBeDisabled()
  },
}

export const DefaultTop: Story = {
  args: {
    label: 'Label',
    placeholder: 'Label',
    name: 'foo',
    required: false,
    labelPosition: 'top',
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).not.toBeDisabled()

    expect(textbox).toHaveValue('')

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('Hello World')

    await userEvent.clear(textbox)
    expect(textbox).toHaveValue('')
  },
}

export const FilledTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    value: 'Value',
    labelPosition: 'top',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')

    expect(textbox).not.toBeDisabled()
  },
}

export const ErrorTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    isInvalid: true,
    placeholder: 'Label',
    labelPosition: 'top',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveAttribute('aria-invalid', 'true')
    expect(textbox).not.toBeDisabled()
  },
}

export const ErrorFilledTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    isInvalid: true,
    value: 'Value',
    labelPosition: 'top',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    expect(textbox).toHaveAttribute('aria-invalid', 'true')
    expect(textbox).not.toBeDisabled()
  },
}

export const DisabledTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    disabled: true,
    labelPosition: 'top',
    placeholder: 'Label',
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('')
    expect(textbox).toBeDisabled()

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('')
  },
}

export const DisabledFilledTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    disabled: true,
    value: 'Value',
    labelPosition: 'top',
  },

  play: async ({ canvas, userEvent }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    expect(textbox).toBeDisabled()

    await userEvent.type(textbox, 'Hello World')
    expect(textbox).toHaveValue('Value')
  },
}

export const WarningDefaultTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    validationState: 'warning',
    labelPosition: 'top',
    placeholder: 'Label',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    // data-validation-state is on the parent label element, not the input
    const container = textbox.closest('[data-validation-state]')
    expect(container?.getAttribute('data-validation-state')).toBe('warning')
    expect(textbox).not.toBeDisabled()
  },
}

export const WarningFilledTop: Story = {
  args: {
    label: 'Label',
    name: 'foo',
    // @ts-expect-error Input does not support this, but wrapping <TextField> does
    validationState: 'warning',
    value: 'Value',
    labelPosition: 'top',
  },

  play: async ({ canvas }) => {
    const textbox = canvas.getByRole('textbox')
    expect(textbox).toHaveValue('Value')
    // data-validation-state is on the parent label element, not the input
    const container = textbox.closest('[data-validation-state]')
    expect(container?.getAttribute('data-validation-state')).toBe('warning')
    expect(textbox).not.toBeDisabled()
  },
}
