import test from 'node:test'
import assert from 'node:assert/strict'
import http from 'node:http'

import { cloudtentaclesRequest, isHighFrequencyMessage } from './http-client.js'

type MockResponse = {
  status?: number
  body?: unknown
}

test('isHighFrequencyMessage recognizes chinese 110001 and english high-frequency', () => {
  assert.equal(
    isHighFrequencyMessage('未知错误码: (110001) 抱歉，请求过于频繁，请稍后再试！谢谢！', -1),
    true,
  )
  assert.equal(isHighFrequencyMessage('high-frequency Request'), true)
  assert.equal(isHighFrequencyMessage('ok', 0), false)
  assert.equal(isHighFrequencyMessage('其他错误', 1), false)
})

test('cloudtentaclesRequest retries high-frequency business errors', async () => {
  let callCount = 0
  const server = await createMockServer(() => {
    callCount += 1
    return {
      body:
        callCount === 1 ? { code: 1, message: 'high-frequency Request' } : { code: 0, data: 'ok' },
    }
  })

  try {
    const result = await cloudtentaclesRequest('/user/get_asset', {
      baseUrl: server.baseUrl,
      token: 'retry-token',
      sourceKey: 'retry-source',
      rateLimitIntervalMs: 0,
      rateLimitRetries: 1,
      rateLimitRetryDelayMs: 1,
    })

    assert.equal(callCount, 2)
    assert.equal(result.payload?.data, 'ok')
  } finally {
    await server.close()
  }
})

test('cloudtentaclesRequest retries chinese 110001 rate-limit messages', async () => {
  let callCount = 0
  const server = await createMockServer(() => {
    callCount += 1
    return {
      body:
        callCount === 1
          ? {
              code: -1,
              message: '未知错误码: (110001) 抱歉，请求过于频繁，请稍后再试！谢谢！',
            }
          : { code: 0, data: 'ok' },
    }
  })

  try {
    const result = await cloudtentaclesRequest('/vn/bind_info', {
      baseUrl: server.baseUrl,
      token: 'cn-retry-token',
      sourceKey: 'cn-retry-source',
      rateLimitIntervalMs: 0,
      rateLimitRetries: 1,
      rateLimitRetryDelayMs: 1,
    })

    assert.equal(callCount, 2)
    assert.equal(result.payload?.data, 'ok')
  } finally {
    await server.close()
  }
})

test('cloudtentaclesRequest maps high-frequency exhaustion to 429', async () => {
  const server = await createMockServer(() => ({
    body: { code: 1, message: 'high-frequency Request' },
  }))

  try {
    await assert.rejects(
      cloudtentaclesRequest('/user/get_asset', {
        baseUrl: server.baseUrl,
        token: 'limited-token',
        sourceKey: 'limited-source',
        rateLimitIntervalMs: 0,
        rateLimitRetries: 0,
      }),
      (error) => {
        assert.equal(error instanceof Error, true)
        assert.equal(Reflect.get(error as object, 'statusCode'), 429)
        assert.equal(Reflect.get(error as object, 'errorCode'), 'cloudtentacles_rate_limited')
        return true
      },
    )
  } finally {
    await server.close()
  }
})

test('cloudtentaclesRequest spaces concurrent requests in the same bucket', async () => {
  const requestTimes: number[] = []
  const server = await createMockServer(() => {
    requestTimes.push(Date.now())
    return { body: { code: 0, data: 'ok' } }
  })

  try {
    await Promise.all([
      cloudtentaclesRequest('/sku/list', {
        baseUrl: server.baseUrl,
        token: 'throttle-token',
        sourceKey: 'throttle-source',
        rateLimitIntervalMs: 25,
        rateLimitRetries: 0,
      }),
      cloudtentaclesRequest('/user/get_knapsack', {
        baseUrl: server.baseUrl,
        token: 'throttle-token',
        sourceKey: 'throttle-source',
        rateLimitIntervalMs: 25,
        rateLimitRetries: 0,
      }),
    ])

    assert.equal(requestTimes.length, 2)
    assert.ok(Math.abs(Number(requestTimes[1]) - Number(requestTimes[0])) >= 20)
  } finally {
    await server.close()
  }
})

async function createMockServer(handler: () => MockResponse) {
  const server = http.createServer((_request, response) => {
    const result = handler()
    response.statusCode = result.status || 200
    response.setHeader('content-type', 'application/json')
    response.end(JSON.stringify(result.body ?? { code: 0, data: null }))
  })

  await new Promise<void>((resolve) => {
    server.listen(0, '127.0.0.1', resolve)
  })

  const address = server.address()
  assert.equal(typeof address, 'object')
  assert.notEqual(address, null)

  return {
    baseUrl: `http://127.0.0.1:${address?.port}`,
    close: () =>
      new Promise<void>((resolve, reject) => {
        server.close((error) => {
          if (error) {
            reject(error)
            return
          }

          resolve()
        })
      }),
  }
}
