import { describe, test, vi, expect, beforeEach } from 'vitest'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { LassoToolsInlineUI } from './lasso-tool-inline'
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('LassoToolsInlineUI', () => {
  const mockOnChangeMode = vi.fn()
  const mockOnChipToggle = vi.fn()
  const mockOnDelete = vi.fn()
  const mockOnAllChipToggle = vi.fn()
  const mockOnAllDelete = vi.fn()

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

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

      const modeButton = within(container).getByLabelText(mode)
      await userEvent.click(modeButton)

      expect(mockOnChangeMode).toHaveBeenCalled()
    })
  }

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

    const showAllButton = within(container).getByText('Show all')
    await userEvent.click(showAllButton)

    expect(mockOnAllChipToggle).toHaveBeenCalled()
  })

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

    const deleteAllButton = within(container).getByText('Remove all')
    await userEvent.click(deleteAllButton)

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

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

describe('LassoToolsInlineUI branches', () => {
  const mockOnChipToggle = vi.fn()
  const mockOnDelete = vi.fn()
  const mockOnAllChipToggle = vi.fn()
  const mockOnAllDelete = vi.fn()
  const mockOnChangeMode = vi.fn()
  const requiredCallbacks = {
    onChangeMode: mockOnChangeMode,
    onAllChipToggle: mockOnAllChipToggle,
    onAllDelete: mockOnAllDelete,
  }

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

  test('renders empty-state NoData when there are no values', () => {
    const { container } = render(
      <LassoToolsInlineUI
        enabled
        values={[]}
        modes={
          DEFAULT_LASSO_TOOLS_MODES as Partial<LassoToolsModes<LassoToolsMode>>
        }
        modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
        modeSelected='polygon'
        onChipToggle={mockOnChipToggle}
        onDelete={mockOnDelete}
        {...requiredCallbacks}
      />,
    )
    // The "no data" Typography branch should have rendered
    expect(container.querySelector('.inline')).toBeDefined()
  })

  test('renders without onChangeMode prop (optional callback)', async () => {
    const { container } = render(
      <LassoToolsInlineUI
        enabled
        values={[{ id: 'p1', label: 'Polygon 1' }]}
        modes={
          DEFAULT_LASSO_TOOLS_MODES as Partial<LassoToolsModes<LassoToolsMode>>
        }
        modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
        modeSelected='polygon'
        onChipToggle={mockOnChipToggle}
        onDelete={mockOnDelete}
        {...requiredCallbacks}
      />,
    )
    // Clicking the mode should not throw despite missing onChangeMode
    const modeButton = within(container).getByLabelText('rectangle')
    await expect(userEvent.click(modeButton)).resolves.toBeUndefined()
  })

  test('uses the first mode when modeSelected is not in the modes map', () => {
    expect(() =>
      render(
        <LassoToolsInlineUI
          enabled
          values={[]}
          modes={
            DEFAULT_LASSO_TOOLS_MODES as Partial<
              LassoToolsModes<LassoToolsMode>
            >
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          // bogus mode → modeExists=false → falls back to first key
          modeSelected={'not-a-mode' as never}
          onChipToggle={mockOnChipToggle}
          {...requiredCallbacks}
          onDelete={mockOnDelete}
        />,
      ),
    ).not.toThrow()
  })

  test('renders with modeSelectedValue=null when disabled (enabled=false)', () => {
    expect(() =>
      render(
        <LassoToolsInlineUI
          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}
          {...requiredCallbacks}
          onDelete={mockOnDelete}
        />,
      ),
    ).not.toThrow()
  })

  test('Actions block is hidden when there is only one value (Actions returns null branch)', () => {
    const { container } = render(
      <LassoToolsInlineUI
        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}
      />,
    )
    // With ≤1 values, the "Show all / Delete all" buttons should not render
    expect(within(container).queryByText(/Show all/i)).toBeNull()
    expect(within(container).queryByText(/Delete all/i)).toBeNull()
  })

  test('uses custom labels.actions when provided', () => {
    const { container } = render(
      <LassoToolsInlineUI
        enabled
        values={[
          { id: 'p1', label: 'P1', visible: false },
          { id: 'p2', label: 'P2', visible: false },
        ]}
        modes={
          DEFAULT_LASSO_TOOLS_MODES as Partial<LassoToolsModes<LassoToolsMode>>
        }
        modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
        modeSelected='polygon'
        labels={{
          actions: {
            toggleAll: {
              active: 'Hide everything',
              inactive: 'Show all (custom)',
            },
            deleteAll: 'Wipe everything',
          },
        }}
        onChipToggle={mockOnChipToggle}
        onDelete={mockOnDelete}
        {...requiredCallbacks}
      />,
    )
    expect(within(container).queryByText('Show all (custom)')).not.toBeNull()
    expect(within(container).queryByText('Wipe everything')).not.toBeNull()
  })

  test('renders a single mode without the ModeList title (data.length <= 1)', () => {
    expect(() =>
      render(
        <LassoToolsInlineUI
          enabled
          values={[]}
          modes={
            {
              polygon: DEFAULT_LASSO_TOOLS_MODES.polygon,
            } as Partial<LassoToolsModes<LassoToolsMode>>
          }
          modesMapping={DEFAULT_LASSO_TOOLS_MODES_MAPPING}
          modeSelected='polygon'
          onChipToggle={mockOnChipToggle}
          {...requiredCallbacks}
          onDelete={mockOnDelete}
        />,
      ),
    ).not.toThrow()
  })
})

const Component = ({
  enabled,
  modeSelected,
  mockOnChangeMode,
  mockOnChipToggle,
  mockOnDelete,
  mockOnAllChipToggle,
  mockOnAllDelete,
}: ComponentProps) => {
  return (
    <LassoToolsInlineUI
      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}
      onChangeMode={mockOnChangeMode}
      onChipToggle={mockOnChipToggle}
      onDelete={mockOnDelete}
      onAllChipToggle={mockOnAllChipToggle}
      onAllDelete={mockOnAllDelete}
    />
  )
}
