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

import { buildCallbackSign } from './sign.js'
import { verifyAffiliateDashCallback } from './verify-callback.js'

const CALLBACK_SECRET = 'cb_test_secret_abc'

function makeCallback(input: {
  rawBody: string
  timestamp: string
  eventId?: string | undefined
  sign?: string | undefined
}) {
  const sign =
    input.sign !== undefined
      ? input.sign
      : buildCallbackSign({
          callbackSecret: CALLBACK_SECRET,
          rawBody: input.rawBody,
          timestamp: input.timestamp,
        })

  return {
    headers: {
      'x-event-id': input.eventId || 'evt-1',
      'x-timestamp': input.timestamp,
      'x-sign': sign,
    },
    rawBody: input.rawBody,
  }
}

test('verifyAffiliateDashCallback accepts a valid signed callback', () => {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const rawBody = JSON.stringify({
    event_id: 'evt-1',
    event: 'order.shipping.updated',
    occurred_at: '2026-08-05T14:30:00+08:00',
    data: { order_no: 'FO1', order_status: 'delivered' },
  })
  const result = verifyAffiliateDashCallback(
    makeCallback({ rawBody, timestamp, eventId: 'evt-1' }),
    { callbackSecret: CALLBACK_SECRET, timestampToleranceSeconds: 300 },
  )

  assert.equal(result.eventId, 'evt-1')
  assert.equal(result.event, 'order.shipping.updated')
  assert.equal(result.data.order_no, 'FO1')
  assert.equal(result.data.order_status, 'delivered')
})

test('verifyAffiliateDashCallback rejects tampered body', () => {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const rawBody = '{"event":"order.shipping.updated"}'
  const tampered = '{"event":"order.cancelled"}'
  const input = makeCallback({ rawBody, timestamp })
  input.rawBody = tampered

  assert.throws(
    () => verifyAffiliateDashCallback(input, { callbackSecret: CALLBACK_SECRET }),
    (error: Error & { errorCode?: string }) =>
      error.errorCode === 'affiliate_dash_callback_sign_invalid',
  )
})

test('verifyAffiliateDashCallback rejects timestamp beyond tolerance', () => {
  const oldTimestamp = String(Math.floor(Date.now() / 1000) - 3600)
  const rawBody = '{"event":"order.shipping.updated"}'

  assert.throws(
    () =>
      verifyAffiliateDashCallback(makeCallback({ rawBody, timestamp: oldTimestamp }), {
        callbackSecret: CALLBACK_SECRET,
        timestampToleranceSeconds: 300,
      }),
    (error: Error & { errorCode?: string }) =>
      error.errorCode === 'affiliate_dash_callback_timestamp_skew',
  )
})

test('verifyAffiliateDashCallback rejects missing sign header', () => {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const rawBody = '{"event":"order.shipping.updated"}'
  const input = makeCallback({ rawBody, timestamp })
  delete input.headers['x-sign']

  assert.throws(
    () => verifyAffiliateDashCallback(input, { callbackSecret: CALLBACK_SECRET }),
    (error: Error & { errorCode?: string }) =>
      error.errorCode === 'affiliate_dash_callback_sign_missing',
  )
})

test('verifyAffiliateDashCallback rejects missing raw body', () => {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const input = makeCallback({ rawBody: '{}', timestamp })
  input.rawBody = undefined

  assert.throws(
    () => verifyAffiliateDashCallback(input, { callbackSecret: CALLBACK_SECRET }),
    (error: Error & { errorCode?: string }) =>
      error.errorCode === 'affiliate_dash_callback_raw_body_missing',
  )
})

test('verifyAffiliateDashCallback passes non-JSON body as long as sign matches', () => {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const rawBody = 'not-json'

  const result = verifyAffiliateDashCallback(makeCallback({ rawBody, timestamp }), {
    callbackSecret: CALLBACK_SECRET,
  })

  assert.equal(result.event, '')
  assert.deepEqual(result.data, {})
})
