import { describe, it, expect } from 'vitest'
import {
  inferSourceFromId,
  cotCategoryFromType,
  isInBbox,
  matchesLayerFilter,
  openSkyStateToCot,
  aisStreamMessageToCot,
  unionBboxes,
  clampBbox,
  parseBboxParam,
  parseLayersParam,
} from '../../../server/utils/cotEntityUtils.js'

describe('cotEntityUtils', () => {
  it('infers source from UID prefix', () => {
    expect(inferSourceFromId('ICAO.abc123')).toBe('adsb')
    expect(inferSourceFromId('MMSI.366123456')).toBe('ais')
    expect(inferSourceFromId('ANDROID-deadbeef')).toBe('tak')
  })

  it('maps CoT type to category', () => {
    expect(cotCategoryFromType('a-f-A-C-F')).toBe('air')
    expect(cotCategoryFromType('a-f-S-C')).toBe('surface')
    expect(cotCategoryFromType('a-f-G-U-C')).toBe('ground')
  })

  it('checks bbox membership', () => {
    const bbox = { west: -123, south: 37, east: -122, north: 38 }
    expect(isInBbox({ lat: 37.5, lng: -122.5 }, bbox)).toBe(true)
    expect(isInBbox({ lat: 40, lng: -122.5 }, bbox)).toBe(false)
  })

  it('filters by layer set', () => {
    const airOnly = new Set(['air'])
    expect(matchesLayerFilter(airOnly, { type: 'a-f-A-C-F' })).toBe(true)
    expect(matchesLayerFilter(airOnly, { type: 'a-f-S-C' })).toBe(false)
  })

  it('maps OpenSky state vector to CoT', () => {
    const state = ['abc123', 'UAL123  ', 'United States', 1, 2, -122.4, 37.7, 10000, false, 200, 90, 5, null, null, 1200, false, 0, 0]
    const cot = openSkyStateToCot(state)
    expect(cot).toMatchObject({
      id: 'ICAO.abc123',
      lat: 37.7,
      lng: -122.4,
      label: 'UAL123',
      source: 'adsb',
      type: 'a-f-A-C-F',
      icao: 'abc123',
      originCountry: 'United States',
      heading: 90,
      speed: 200,
      altitude: 10000,
      verticalRate: 5,
      squawk: '1200',
    })
  })

  it('maps OpenSky rotorcraft to helicopter CoT type', () => {
    const state = ['heli01', 'N123HC  ', 'United States', 1, 2, -122.4, 37.7, 500, false, 50, 180, 0, null, null, null, false, 0, 8]
    const cot = openSkyStateToCot(state)
    expect(cot?.type).toBe('a-f-A-C-H')
  })

  it('maps AISStream message to CoT', () => {
    const cot = aisStreamMessageToCot({
      MetaData: { MMSI: 366123456, ShipName: 'TEST SHIP' },
      Message: {
        PositionReport: {
          UserID: 366123456,
          Latitude: 37.8,
          Longitude: -122.3,
          Sog: 12.5,
          Cog: 180,
        },
      },
    })
    expect(cot).toMatchObject({
      id: 'MMSI.366123456',
      lat: 37.8,
      lng: -122.3,
      label: 'TEST SHIP',
      source: 'ais',
      type: 'a-f-S-C',
    })
  })

  it('unions bboxes', () => {
    expect(unionBboxes([
      { west: -123, south: 37, east: -122, north: 38 },
      { west: -124, south: 36, east: -121, north: 39 },
    ])).toEqual({ west: -124, south: 36, east: -121, north: 39 })
  })

  it('clamps oversized bbox to max span', () => {
    const huge = { west: -125, south: 32, east: -115, north: 42 }
    const clamped = clampBbox(huge, 10)
    expect(clamped.north - clamped.south).toBeCloseTo(10)
    expect(clamped.east - clamped.west).toBeCloseTo(10)
    expect((clamped.north + clamped.south) / 2).toBeCloseTo(37)
    expect((clamped.east + clamped.west) / 2).toBeCloseTo(-120)
  })

  it('parses bbox and layers query params', () => {
    expect(parseBboxParam('-123,37,-122,38')).toEqual({ west: -123, south: 37, east: -122, north: 38 })
    expect(parseBboxParam('bad')).toBeNull()
    expect(parseLayersParam('air,surface')).toEqual(new Set(['air', 'surface']))
    expect(parseLayersParam('none').size).toBe(0)
  })
})
