import { describe, expect, it } from 'vitest'
import { MagentoClient, MagentoApiError } from './client.js'
import { oauth1Header } from './oauth1.js'
import { and, toQuery } from './search-criteria.js'

describe('toQuery', () => {
  it('serialises filter groups, sort orders and paging in Magento bracket syntax', () => {
    const q = toQuery({
      filterGroups: and({ field: 'updated_at', value: '2026-01-01 00:00:00', condition: 'gteq' }, { field: 'entity_id', value: [1, 2], condition: 'in' }),
      sortOrders: [{ field: 'updated_at', direction: 'ASC' }],
      pageSize: 200,
      currentPage: 3,
    })
    expect(q).toEqual({
      'searchCriteria[filterGroups][0][filters][0][field]': 'updated_at',
      'searchCriteria[filterGroups][0][filters][0][value]': '2026-01-01 00:00:00',
      'searchCriteria[filterGroups][0][filters][0][conditionType]': 'gteq',
      'searchCriteria[filterGroups][1][filters][0][field]': 'entity_id',
      'searchCriteria[filterGroups][1][filters][0][value]': '1,2',
      'searchCriteria[filterGroups][1][filters][0][conditionType]': 'in',
      'searchCriteria[sortOrders][0][field]': 'updated_at',
      'searchCriteria[sortOrders][0][direction]': 'ASC',
      'searchCriteria[pageSize]': 200,
      'searchCriteria[currentPage]': 3,
    })
  })

  it('always sends pageSize so list endpoints accept the request', () => {
    expect(toQuery()).toEqual({ 'searchCriteria[pageSize]': 100 })
  })
})

describe('oauth1Header', () => {
  it('is deterministic for a fixed nonce/timestamp and includes query params in the signature', () => {
    const creds = { consumerKey: 'ck', consumerSecret: 'cs', accessToken: 'at', accessTokenSecret: 'ats' }
    const a = oauth1Header('GET', 'https://m.test/rest/all/V1/products?searchCriteria[pageSize]=1', creds, 'n', '1')
    const b = oauth1Header('GET', 'https://m.test/rest/all/V1/products?searchCriteria[pageSize]=1', creds, 'n', '1')
    const c = oauth1Header('GET', 'https://m.test/rest/all/V1/products?searchCriteria[pageSize]=2', creds, 'n', '1')
    expect(a).toBe(b)
    expect(a).not.toBe(c)
    expect(a).toMatch(/^OAuth oauth_consumer_key="ck", oauth_nonce="n", oauth_signature="[^"]+", oauth_signature_method="HMAC-SHA256"/)
  })
})

describe('MagentoClient', () => {
  const json = (body: unknown, status = 200) => new Response(JSON.stringify(body), { status })

  it('paginates by total_count, not by empty pages (Magento repeats the last page)', async () => {
    const pages = [[{ id: 1 }, { id: 2 }], [{ id: 3 }]]
    const calls: string[] = []
    const client = new MagentoClient({
      baseUrl: 'https://m.test',
      accessToken: 't',
      fetch: async url => {
        calls.push(String(url))
        const page = Number(new URL(String(url)).searchParams.get('searchCriteria[currentPage]'))
        return json({ items: pages[Math.min(page, 2) - 1], total_count: 3 })
      },
    })
    const seen: number[] = []
    for await (const items of client.paginate<{ id: number }>('/V1/products', { pageSize: 2 })) seen.push(...items.map(i => i.id))
    expect(seen).toEqual([1, 2, 3])
    expect(calls).toHaveLength(2)
  })

  it('retries transient errors and surfaces Magento messages with parameters substituted', async () => {
    let n = 0
    const client = new MagentoClient({
      baseUrl: 'https://m.test',
      accessToken: 't',
      maxRetries: 2,
      fetch: async () => (++n < 2 ? new Response('busy', { status: 503, headers: { 'retry-after': '0' } }) : json({ message: 'The "%1" SKU was not found', parameters: ['X'] }, 404)),
    })
    const err = await client.get('/V1/products/X').catch(e => e as MagentoApiError)
    expect(n).toBe(2)
    expect(err).toBeInstanceOf(MagentoApiError)
    expect(err.status).toBe(404)
    expect(err.message).toContain('The "X" SKU was not found')
  })

  it('sends a bearer token unless full OAuth1 credentials are configured', async () => {
    let auth = ''
    const client = new MagentoClient({ baseUrl: 'https://m.test', accessToken: 'tok', fetch: async (_u, init) => ((auth = String((init?.headers as Record<string, string>).Authorization)), json([])) })
    await client.get('/V1/store/storeViews')
    expect(auth).toBe('Bearer tok')
  })
})
