/* eslint-disable test/no-import-node-test */
import assert from 'node:assert/strict'
import test from 'node:test'
import {
  assertSafeOutputPath,
  buildAmapSearchParams,
  buildQueryPlan,
  dedupePois,
  normalizeAmapPage,
  normalizeAmapPoi,
  parseAmapLocation,
  parseCliArgs,
} from '../scripts/fetch-amap-pois.mjs'

const typeQuery = {
  id: 'types:parks',
  kind: 'types',
  label: 'parks',
  types: ['110101'],
}

const keywordQuery = {
  id: 'keyword:1',
  kind: 'keyword',
  label: 'greenway',
  keyword: 'greenway',
}

function rawPoi(overrides = {}) {
  return {
    id: 'B001',
    name: 'Test Park',
    parent: [],
    location: '113.935,22.748',
    type: 'Tourist Attraction;Park',
    typecode: '110101',
    pname: 'Guangdong Province',
    pcode: '440000',
    cityname: 'Shenzhen',
    citycode: '0755',
    adname: 'Guangming District',
    adcode: '440311',
    address: ['Park Road'],
    business: {
      alias: [],
      tel: '0755-12345678',
      rating: '4.7',
      opentime_today: '06:00-22:00',
      opentime_week: 'Mon-Sun 06:00-22:00',
    },
    photos: [
      { title: 'Gate', url: 'https://example.invalid/gate.jpg' },
      { title: 'Duplicate', url: 'https://example.invalid/gate.jpg' },
    ],
    ...overrides,
  }
}

test('parseAmapLocation accepts valid coordinates and rejects malformed values', () => {
  assert.deepEqual(parseAmapLocation('113.935,22.748'), {
    longitude: 113.935,
    latitude: 22.748,
  })
  assert.equal(parseAmapLocation('113.935'), null)
  assert.equal(parseAmapLocation('181,22.748'), null)
})

test('normalizeAmapPoi maps only reviewed candidate fields without raw response data', () => {
  const normalized = normalizeAmapPoi(rawPoi(), typeQuery)

  assert.ok(normalized)
  assert.equal(normalized.id, 'B001')
  assert.equal(normalized.coordinateSystem, 'GCJ02')
  assert.equal(normalized.address, 'Park Road')
  assert.equal(normalized.parentId, null)
  assert.deepEqual(normalized.business, {
    alias: null,
    telephone: '0755-12345678',
    sourceRating: 4.7,
    openingHoursToday: '06:00-22:00',
    openingHoursWeek: 'Mon-Sun 06:00-22:00',
  })
  assert.deepEqual(normalized.photos, [
    { title: 'Gate', url: 'https://example.invalid/gate.jpg' },
  ])
  assert.deepEqual(normalized.matchedQueries, [{
    id: 'types:parks',
    kind: 'types',
    label: 'parks',
  }])
  assert.equal('raw' in normalized, false)
})

test('normalizeAmapPage applies exact adcode filtering and rejects unusable records', () => {
  const page = normalizeAmapPage([
    rawPoi(),
    rawPoi({ id: 'B002', adcode: '440309' }),
    rawPoi({ id: 'B003', location: [] }),
  ], typeQuery)

  assert.deepEqual(page.pois.map(poi => poi.id), ['B001'])
  assert.equal(page.filteredOutByAdcode, 1)
  assert.equal(page.rejectedInvalid, 1)
})

test('dedupePois merges matches and unique photos by AMap ID without mutating inputs', () => {
  const first = normalizeAmapPoi(rawPoi(), typeQuery)
  const second = normalizeAmapPoi(rawPoi({
    address: [],
    photos: [
      { title: [], url: 'https://example.invalid/gate.jpg' },
      { title: 'Lake', url: 'https://example.invalid/lake.jpg' },
    ],
  }), keywordQuery)
  assert.ok(first)
  assert.ok(second)
  const firstSnapshot = JSON.stringify(first)
  const secondSnapshot = JSON.stringify(second)

  const result = dedupePois([first, second])

  assert.equal(result.length, 1)
  assert.equal(result[0].id, 'B001')
  assert.equal(result[0].address, 'Park Road')
  assert.equal(result[0].sourceOccurrences, 2)
  assert.deepEqual(result[0].matchedQueries.map(query => query.id), [
    'types:parks',
    'keyword:1',
  ])
  assert.deepEqual(result[0].photos, [
    { title: 'Gate', url: 'https://example.invalid/gate.jpg' },
    { title: 'Lake', url: 'https://example.invalid/lake.jpg' },
  ])
  assert.equal(JSON.stringify(first), firstSnapshot)
  assert.equal(JSON.stringify(second), secondSnapshot)
})

test('buildQueryPlan keeps type scans and keyword recall as separate requests', () => {
  const plan = buildQueryPlan(
    [{ name: 'parks', types: ['110101', '110103'] }],
    ['greenway'],
  )

  assert.deepEqual(plan, [
    {
      id: 'types:parks',
      kind: 'types',
      label: 'parks',
      types: ['110101', '110103'],
    },
    {
      id: 'keyword:1',
      kind: 'keyword',
      label: 'greenway',
      keyword: 'greenway',
    },
  ])
})

test('AMap request parameters always enforce Guangming District and strict city filtering', () => {
  const params = buildAmapSearchParams(typeQuery, 25, 3)

  assert.equal(params.get('region'), '440311')
  assert.equal(params.get('city_limit'), 'true')
  assert.equal(params.get('page_size'), '25')
  assert.equal(params.get('page_num'), '3')
  assert.equal(params.get('types'), '110101')
  assert.equal(params.get('show_fields'), 'business,photos')
  assert.equal(params.has('key'), false)
})

test('CLI rejects key arguments without echoing their value', () => {
  const secret = 'must-not-appear'
  assert.throws(
    () => parseCliArgs([`--key=${secret}`]),
    error => !error.message.includes(secret),
  )
})

test('CLI limits pagination to the AMap 200-result window', () => {
  assert.equal(parseCliArgs(['--no-keywords']).maxPages, 8)
  assert.throws(
    () => parseCliArgs(['--page-size', '25', '--max-pages', '9']),
    /cannot exceed 8/,
  )
})

test('generated output cannot be directed into source or test directories', () => {
  const projectRoot = '/workspace/map-app'
  assert.throws(
    () => assertSafeOutputPath('src/data/candidates.json', projectRoot),
    /Refusing to write/,
  )
  assert.equal(
    assertSafeOutputPath('artifacts/candidates.json', projectRoot),
    '/workspace/map-app/artifacts/candidates.json',
  )
})
