import { describe, test, vi, expect, beforeEach } from 'vitest'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LassoToolsUI } from './lasso-tool'
import { DEFAULT_LASSO_TOOLS_MODES_MAPPING } from './const'
import type { LassoToolsModes } from '../types'
import {
  changeLassoToolsModeOptions,
  DEFAULT_LASSO_TOOLS_MODES,
} from '../../../tests/config'

type LassoToolsMode = 'polygon' | 'rectangle' | 'circle' | 'lasso' | 'edit'

describe('LassoToolsUI', () => {
  const mockOnActionToggle = vi.fn()
  const mockOnChangeMode = vi.fn()
  const mockOnChipToggle = vi.fn()
  const mockOnDelete = vi.fn()
  const mockOnAllChipToggle = vi.fn()
  const mockOnAllDelete = vi.fn()

  beforeEach(() => {
    vi.clearAllMocks()
  })

  test('should enable drawing', async () => {
    const { container } = render(
      <Component
        enabled={false}
        modeSelected='polygon'
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChipToggle={mockOnChipToggle}
        mockOnDelete={mockOnDelete}
        mockOnAllChipToggle={mockOnAllChipToggle}
        mockOnAllDelete={mockOnAllDelete}
      />,
    )

    const polygonButton = within(container).getByLabelText(
      'Click on the map to draw your spatial filter',
    )
    await userEvent.click(polygonButton)

    expect(mockOnActionToggle).toHaveBeenCalledWith(true)
  })

  for (const [mode, label] of Object.entries(changeLassoToolsModeOptions)) {
    test(`should change mode to ${label}`, async () => {
      const { container } = render(
        <Component
          enabled
          modeSelected='polygon'
          mockOnActionToggle={mockOnActionToggle}
          mockOnChangeMode={mockOnChangeMode}
          mockOnChipToggle={mockOnChipToggle}
          mockOnDelete={mockOnDelete}
          mockOnAllChipToggle={mockOnAllChipToggle}
          mockOnAllDelete={mockOnAllDelete}
        />,
      )

      const dropdownButton = within(container).getAllByRole('button')[1]!
      await userEvent.click(dropdownButton)

      const modeButton = screen.getByText(label)
      await userEvent.click(modeButton)

      expect(mockOnChangeMode).toHaveBeenCalledWith(mode)
    })
  }

  test('should change polygons visibility when clicking on the chip', async () => {
    const { container } = render(
      <Component
        enabled
        modeSelected='polygon'
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChipToggle={mockOnChipToggle}
        mockOnDelete={mockOnDelete}
        mockOnAllChipToggle={mockOnAllChipToggle}
        mockOnAllDelete={mockOnAllDelete}
      />,
    )

    const chip = within(container).getAllByLabelText('Show drawing')[0]!
    await userEvent.click(chip)

    expect(mockOnChipToggle).toHaveBeenCalledWith('polygon-id-1', true)
  })

  test('should remove a polygon when clicking on chip-cancel-icon', async () => {
    render(
      <Component
        enabled
        modeSelected='polygon'
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChipToggle={mockOnChipToggle}
        mockOnDelete={mockOnDelete}
        mockOnAllChipToggle={mockOnAllChipToggle}
        mockOnAllDelete={mockOnAllDelete}
      />,
    )

    const cancelButton = screen.getAllByTestId('CancelIcon')[0]!
    await userEvent.click(cancelButton)

    expect(mockOnDelete).toHaveBeenCalledWith('polygon-id-1')
  })

  test('should change polygons visibility when clicking on Show/Hide All', async () => {
    render(
      <Component
        enabled
        modeSelected='polygon'
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChipToggle={mockOnChipToggle}
        mockOnDelete={mockOnDelete}
        mockOnAllChipToggle={mockOnAllChipToggle}
        mockOnAllDelete={mockOnAllDelete}
      />,
    )

    const optionsButton = screen.getByTestId('MoreVertOutlinedIcon')
    await userEvent.click(optionsButton)

    const showAllButton = await screen.findByRole('menuitem', {
      name: 'Show all',
    })
    await userEvent.click(showAllButton)

    expect(mockOnAllChipToggle).toHaveBeenCalled()
  })

  test('should remove all polygons when clicking on Delete All', async () => {
    render(
      <Component
        enabled
        modeSelected='polygon'
        mockOnActionToggle={mockOnActionToggle}
        mockOnChangeMode={mockOnChangeMode}
        mockOnChipToggle={mockOnChipToggle}
        mockOnDelete={mockOnDelete}
        mockOnAllChipToggle={mockOnAllChipToggle}
        mockOnAllDelete={mockOnAllDelete}
      />,
    )

    const optionsButton = screen.getByTestId('MoreVertOutlinedIcon')
    await userEvent.click(optionsButton)

    const deleteAllButton = await screen.findByRole('menuitem', {
      name: 'Remove all',
    })
    await userEvent.click(deleteAllButton)

    expect(mockOnAllDelete).toHaveBeenCalled()
  })
})

describe('LassoToolsUI branches', () => {
  const mockOnChipToggle = vi.fn()
  const mockOnDelete = vi.fn()
  const mockOnAllChipToggle = vi.fn()
  const mockOnAllDelete = vi.fn()
  const mockOnActionToggle = vi.fn()
  const mockOnChangeMode = vi.fn()
  // Required-callback overrides that satisfy the type. Each test below
  // spreads this to pin down the prop set without breaking strict typing.
  const requiredCallbacks = {
    onActionToggle: mockOnActionToggle,
    onChangeMode: mockOnChangeMode,
    onAllChipToggle: mockOnAllChipToggle,
    onAllDelete: mockOnAllDelete,
  }

  beforeEach(() => {
    vi.clearAllMocks()
  })

  test('renders empty-state when no values', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('renders with enabled=false (modeSelectedValue=null branch)', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled={false}
          values={[{ id: 'p1', label: 'P1' }]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('falls back to first mode when modeSelected is unknown', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected={'unknown' as never}
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('renders without onActionToggle / onChangeMode (optional callbacks)', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled
          values={[{ id: 'p1', label: 'P1' }]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('hides SecondaryActions when there is only one value', () => {
    const { container } = render(
      <LassoToolsUI
        enabled
        values={[{ id: 'p1', label: 'Only one' }]}
        modes={
          DEFAULT_LASSO_TOOLS_MODES as Partial<LassoToolsModes<LassoToolsMode>>
        }
        modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
        modeSelected='polygon'
        onChipToggle={mockOnChipToggle}
        onDelete={mockOnDelete}
        {...requiredCallbacks}
      />,
    )
    // SecondaryActions slot should not render its menu trigger
    expect(within(container).queryByLabelText('More')).toBeNull()
  })

  test('applies custom PaperProps.sx override', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          PaperProps={
            { sx: { backgroundColor: 'red' }, 'data-x': 'y' } as never
          }
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('renders with custom labels.action / labels.options overrides', () => {
    expect(() =>
      render(
        <LassoToolsUI
          enabled
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          labels={
            {
              action: { enable: 'On', disable: 'Off' },
              options: { mode: { title: 'Pick mode' } },
            } as never
          }
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
        />,
      ),
    ).not.toThrow()
  })

  test('renders an action toggle when onActionToggle is provided', () => {
    const onActionToggle = vi.fn()
    expect(() =>
      render(
        <LassoToolsUI
          enabled={false}
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          onChipToggle={mockOnChipToggle}
          onDelete={mockOnDelete}
          {...requiredCallbacks}
          onActionToggle={onActionToggle}
        />,
      ),
    ).not.toThrow()
  })
})

// Aux
interface ComponentProps {
  enabled: boolean
  modeSelected: LassoToolsMode
  mockOnActionToggle: () => void
  mockOnChangeMode: () => void
  mockOnChipToggle: () => void
  mockOnDelete: () => void
  mockOnAllChipToggle: () => void
  mockOnAllDelete: () => void
}

const Component = ({
  enabled,
  modeSelected,
  mockOnActionToggle,
  mockOnChangeMode,
  mockOnChipToggle,
  mockOnDelete,
  mockOnAllChipToggle,
  mockOnAllDelete,
}: ComponentProps) => {
  return (
    <LassoToolsUI
      enabled={enabled}
      values={[
        {
          id: 'polygon-id-1',
          label: 'Polygon 1',
        },
        {
          id: 'polygon-id-2',
          label: 'Polygon 2',
        },
      ]}
      modes={
        DEFAULT_LASSO_TOOLS_MODES as Partial<LassoToolsModes<LassoToolsMode>>
      }
      modeSelected={modeSelected}
      modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
      onActionToggle={mockOnActionToggle}
      onChangeMode={mockOnChangeMode}
      onChipToggle={mockOnChipToggle}
      onDelete={mockOnDelete}
      onAllChipToggle={mockOnAllChipToggle}
      onAllDelete={mockOnAllDelete}
    />
  )
}
