import { describe, it, expect, vi } from 'vitest'
import { getMeasurementToolsLayer } from './layer'
import { CustomEditableGeojsonLayer } from '../custom-editable-geojson-layer'
import { MapMeasurementUnit } from './const'
import type { GeoJsonEditMode } from '@deck.gl-community/editable-layers'
import type { EditableGeoJsonLayerProps } from '@deck.gl-community/editable-layers'

// Helper type to access the actual layer props structure
type MeasurementLayerProps = EditableGeoJsonLayerProps & {
  mode: GeoJsonEditMode
  data: unknown[]
  _subLayerProps?: {
    tooltips?: {
      getText?: (properties: { text: number }) => string
    }
    guides?: {
      getLineColor?: [number, number, number, number]
      getFillColor?: [number, number, number, number]
    }
  }
  getEditHandlePointColor?: [number, number, number, number]
  getEditHandlePointOutlineColor?: [number, number, number, number]
}

// Mock mode type with custom getTooltipText method
type MockMode = GeoJsonEditMode & {
  getTooltipText: ReturnType<typeof vi.fn>
}

describe('getMeasurementToolsLayer', () => {
  const mockMode: MockMode = {
    getTooltipText: vi.fn(({ meters }) => `${meters}m`),
  } as MockMode

  it('should create layer with basic props', () => {
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    expect(layer.type).toBe(CustomEditableGeojsonLayer)
    expect(layer.props).toBeDefined()

    const props = layer.props as MeasurementLayerProps
    expect(props.mode).toBe(mockMode)
    expect(props.data).toEqual([])
  })

  it('should set enabled state correctly', () => {
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    expect(layer.props.visible).toBe(true)
    expect(layer.props.pickable).toBe(true)
  })

  it('should handle disabled state', () => {
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: false,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    expect(layer.props.visible).toBe(false)
    expect(layer.props.pickable).toBe(false)
  })

  it('should include onEdit when enabled', () => {
    const onEdit = vi.fn()
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit,
    })

    const props = layer.props as MeasurementLayerProps
    expect(props.onEdit).toBe(onEdit)
  })

  it('should not include onEdit when disabled', () => {
    const onEdit = vi.fn()
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: false,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit,
    })

    const props = layer.props as MeasurementLayerProps
    expect(props.onEdit).toBeUndefined()
  })

  it('should merge custom layer props', () => {
    const customProps = {
      id: 'custom-layer-id',
      opacity: 0.5,
    }

    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: customProps,
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    expect(layer.props.id).toBe('custom-layer-id')
    expect(layer.props.opacity).toBe(0.5)
  })

  it('should configure tooltip getText function', () => {
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    const props = layer.props as MeasurementLayerProps
    const getText = props._subLayerProps?.tooltips?.getText
    expect(getText).toBeDefined()

    // Test getText function
    if (getText) {
      getText({ text: 100 })
      expect(mockMode.getTooltipText).toHaveBeenCalledWith({
        meters: 100,
        locale: 'en',
        unit: MapMeasurementUnit.meter,
        options: undefined,
      })
    }
  })

  it('should pass textOptions to tooltip getText', () => {
    const textOptions = { maximumFractionDigits: 2 }
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor: [255, 0, 0, 255],
      pointColor: [0, 0, 255, 255],
      textOptions,
      onEdit: vi.fn(),
    })

    const props = layer.props as MeasurementLayerProps
    const getText = props._subLayerProps?.tooltips?.getText
    if (getText) {
      getText({ text: 100 })
      expect(mockMode.getTooltipText).toHaveBeenCalledWith({
        meters: 100,
        locale: 'en',
        unit: MapMeasurementUnit.meter,
        options: textOptions,
      })
    }
  })

  it('should apply guide colors', () => {
    const guideColor = [255, 128, 0, 255] as [number, number, number, number]
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor,
      pointColor: [0, 0, 255, 255],
      onEdit: vi.fn(),
    })

    const props = layer.props as MeasurementLayerProps
    expect(props._subLayerProps?.guides?.getLineColor).toEqual(guideColor)
    // Fill color should have alpha set to 20
    expect(props._subLayerProps?.guides?.getFillColor).toEqual([
      255, 128, 0, 20,
    ])
  })

  it('should apply point colors', () => {
    const pointColor = [128, 64, 255, 255] as [number, number, number, number]
    const guideColor = [255, 0, 0, 255] as [number, number, number, number]
    const layer = getMeasurementToolsLayer({
      mode: mockMode,
      enabled: true,
      locale: 'en',
      unit: MapMeasurementUnit.meter,
      layerProps: {},
      guideColor,
      pointColor,
      onEdit: vi.fn(),
    })

    const props = layer.props as MeasurementLayerProps
    expect(props.getEditHandlePointColor).toEqual(pointColor)
    expect(props.getEditHandlePointOutlineColor).toEqual(guideColor)
  })
})
