import assert from 'node:assert/strict'

import { apiRequest } from '../src/services/api.js'

async function testUsesCustomContentTypeHeader() {
  let capturedOptions = null

  global.fetch = async (_url, options) => {
    capturedOptions = options
    return {
      ok: true,
      async json() {
        return { ok: true }
      }
    }
  }

  await apiRequest('/knowledge/documents', {
    method: 'POST',
    body: 'payload',
    contentType: 'application/octet-stream'
  })

  assert.equal(capturedOptions.headers['Content-Type'], 'application/octet-stream')
}

async function testSupportsBlobResponses() {
  const blob = new Blob(['preview'])

  global.fetch = async () => ({
    ok: true,
    async blob() {
      return blob
    },
    async json() {
      throw new Error('json parser should not be used for blob responses')
    }
  })

  const payload = await apiRequest('/knowledge/documents/demo/content', {
    responseType: 'blob',
    contentType: null
  })

  assert.equal(payload, blob)
}

async function testInjectsAuthenticatedUserHeaders() {
  const sessionStorage = new Map([
    [
      'x-financial-auth-user',
      JSON.stringify({
        username: 'admin',
        name: 'Admin User',
        roleCodes: ['manager'],
        isAdmin: true
      })
    ]
  ])

  global.window = {
    sessionStorage: {
      getItem(key) {
        return sessionStorage.get(key) ?? null
      }
    }
  }

  let capturedOptions = null

  global.fetch = async (_url, options) => {
    capturedOptions = options
    return {
      ok: true,
      async json() {
        return { ok: true }
      }
    }
  }

  await apiRequest('/knowledge/library')

  assert.equal(capturedOptions.headers['x-auth-username'], 'admin')
  assert.equal(capturedOptions.headers['x-auth-name'], 'Admin User')
  assert.equal(capturedOptions.headers['x-auth-role-codes'], 'manager')
  assert.equal(capturedOptions.headers['x-auth-is-admin'], 'true')
}

async function testFormatsValidationErrors() {
  global.fetch = async () => ({
    ok: false,
    async json() {
      return {
        detail: [
          {
            loc: ['body', 'email'],
            msg: 'value is not a valid email address'
          },
          {
            loc: ['body', 'password'],
            msg: 'String should have at least 5 characters'
          }
        ]
      }
    }
  })

  await assert.rejects(
    () => apiRequest('/employees/demo', { method: 'PATCH', body: '{}' }),
    (error) => {
      assert.equal(
        error.message,
        'email: value is not a valid email address；password: String should have at least 5 characters'
      )
      return true
    }
  )
}

async function testRejectsWithCustomTimeoutMessage() {
  global.fetch = async (_url, options) =>
    new Promise((_, reject) => {
      options.signal.addEventListener('abort', () => {
        const error = new Error('aborted')
        error.name = 'AbortError'
        reject(error)
      })
    })

  await assert.rejects(
    () =>
      apiRequest('/knowledge/library', {
        timeoutMs: 1,
        timeoutMessage: '知识问答整理超时，已停止等待。'
      }),
    (error) => {
      assert.equal(error.message, '知识问答整理超时，已停止等待。')
      assert.equal(error.code, 'REQUEST_TIMEOUT')
      return true
    }
  )
}

async function run() {
  await testUsesCustomContentTypeHeader()
  await testSupportsBlobResponses()
  await testInjectsAuthenticatedUserHeaders()
  await testFormatsValidationErrors()
  await testRejectsWithCustomTimeoutMessage()
  console.log('api-request tests passed')
}

run().catch((error) => {
  console.error(error)
  process.exit(1)
})
