import { describe, it, expect, afterEach } from 'vitest'
import { GeoJsonLayer } from '@deck.gl/layers'
import { TileLayer } from '@deck.gl/geo-layers'
import { VectorTileLayer } from '@deck.gl/carto'
import { withLayerDefaults, layerDefaults } from './with-layer-defaults'
import { isEmptyGeometrySourceError } from './is-empty-geometry-source-error'

describe('withLayerDefaults', () => {
  // Tests below register a temporary VectorTileLayer entry on top of the
  // seeded TileLayer one; remove it so the shared registry doesn't leak.
  afterEach(() => layerDefaults.delete(VectorTileLayer))

  it('injects the TileLayer default into props', () => {
    expect(withLayerDefaults(TileLayer)).toEqual({
      type: TileLayer,
      props: {
        refinementStrategy: 'no-overlap',
        onError: isEmptyGeometrySourceError,
      },
    })
  })

  it('inherits an ancestor default (VectorTileLayer -> MVTLayer -> TileLayer)', () => {
    expect(withLayerDefaults(VectorTileLayer)).toEqual({
      type: VectorTileLayer,
      props: {
        refinementStrategy: 'no-overlap',
        onError: isEmptyGeometrySourceError,
      },
    })
  })

  it('merges the type and its ancestors (union of both prop sets)', () => {
    layerDefaults.set(VectorTileLayer, { uniqueIdProperty: 'geoid' })
    expect(withLayerDefaults(VectorTileLayer)).toEqual({
      type: VectorTileLayer,
      props: {
        refinementStrategy: 'no-overlap', // inherited from TileLayer
        onError: isEmptyGeometrySourceError, // inherited from TileLayer
        uniqueIdProperty: 'geoid', // own
      },
    })
  })

  it("lets the caller's props override a default", () => {
    layerDefaults.set(VectorTileLayer, { opacity: 0.5 })
    expect(
      withLayerDefaults(VectorTileLayer, { props: { opacity: 0.9 } }),
    ).toEqual({
      type: VectorTileLayer,
      props: {
        refinementStrategy: 'no-overlap', // inherited from TileLayer
        onError: isEmptyGeometrySourceError, // inherited from TileLayer
        opacity: 0.9, // caller overrides the VectorTileLayer default of 0.5
      },
    })
  })

  it('lets the caller override the TileLayer onError default', () => {
    const customOnError = () => true
    expect(
      withLayerDefaults(TileLayer, { props: { onError: customOnError } }),
    ).toEqual({
      type: TileLayer,
      props: {
        refinementStrategy: 'no-overlap',
        onError: customOnError,
      },
    })
  })

  it('passes name/order/legend through untouched', () => {
    expect(
      withLayerDefaults(GeoJsonLayer, {
        name: 'My Layer',
        order: 3,
        legend: { switchable: true },
        props: { opacity: 1 },
      }),
    ).toEqual({
      type: GeoJsonLayer,
      name: 'My Layer',
      order: 3,
      legend: { switchable: true },
      props: { opacity: 1 },
    })
  })

  it('returns { type, props: {} } for a layer with no registered default', () => {
    expect(withLayerDefaults(GeoJsonLayer)).toEqual({
      type: GeoJsonLayer,
      props: {},
    })
  })

  it('does not mutate the registry', () => {
    const layer = withLayerDefaults(TileLayer) as {
      props: Record<string, unknown>
    }
    layer.props.refinementStrategy = 'never'
    expect(
      (withLayerDefaults(TileLayer).props as Record<string, unknown>)
        .refinementStrategy,
    ).toBe('no-overlap')
  })
})
