import { describe, it, expect } from 'vitest'
import { createCpClient, CpClientError } from '../src/cp-client.js'

function jsonResponse(body: unknown, status = 200): Response {
  return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })
}

describe('createCpClient.listHosts', () => {
  it('maps the CP host records to a curated view and strips agentPubkey/enrollFpr', async () => {
    const captured: { url?: string; init?: RequestInit | undefined } = {}
    const fetchFn = (async (url: string | URL | Request, init?: RequestInit) => {
      captured.url = String(url)
      captured.init = init
      return jsonResponse([
        {
          hostId: 'h1',
          accountId: 'acct-1',
          subdomain: 'alpha',
          agentPubkey: 'BASE64PUBKEY',
          enrollFpr: 'fpr',
          status: 'online',
          lastSeen: '2026-01-02T00:00:00.000Z',
          createdAt: '2026-01-01T00:00:00.000Z',
          revokedAt: null,
        },
      ])
    }) as typeof fetch

    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    const hosts = await client.listHosts('acct-1', 'TOKEN123')

    expect(captured.url).toBe('http://127.0.0.1:8080/accounts/acct-1/hosts')
    expect((captured.init?.headers as Record<string, string>).authorization).toBe('Bearer TOKEN123')
    expect(hosts).toHaveLength(1)
    expect(hosts[0]).toMatchObject({ hostId: 'h1', subdomain: 'alpha', status: 'online', lastSeen: '2026-01-02T00:00:00.000Z' })
    expect(hosts[0]).not.toHaveProperty('agentPubkey')
    expect(hosts[0]).not.toHaveProperty('enrollFpr')
  })

  it('throws CpClientError carrying the upstream status on non-2xx', async () => {
    const fetchFn = (async () => new Response('nope', { status: 403 })) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    await expect(client.listHosts('acct-1', 't')).rejects.toMatchObject({ name: 'CpClientError', status: 403 })
  })

  it('maps a network failure to a 502 CpClientError', async () => {
    const fetchFn = (async () => {
      throw new Error('ECONNREFUSED')
    }) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    await expect(client.listHosts('acct-1', 't')).rejects.toMatchObject({ status: 502 })
  })
})

describe('createCpClient.createPairingCode', () => {
  it('POSTs and maps { code, expiresAt }', async () => {
    const captured: { init?: RequestInit | undefined } = {}
    const fetchFn = (async (_url: string | URL | Request, init?: RequestInit) => {
      captured.init = init
      return jsonResponse({ code: 'ABCD-EFGH', expiresAt: '2026-02-01T00:00:00.000Z' }, 201)
    }) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    const issued = await client.createPairingCode('acct-1', 'TOK')
    expect(captured.init?.method).toBe('POST')
    expect(issued).toEqual({ code: 'ABCD-EFGH', expiresAt: '2026-02-01T00:00:00.000Z' })
  })

  it('rejects a malformed CP pairing response', async () => {
    const fetchFn = (async () => jsonResponse({ nope: true }, 201)) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    await expect(client.createPairingCode('acct-1', 't')).rejects.toBeInstanceOf(Error)
  })
})

describe('createCpClient.deleteHost', () => {
  it('DELETEs and resolves on 204', async () => {
    const captured: { url?: string; init?: RequestInit | undefined } = {}
    const fetchFn = (async (url: string | URL | Request, init?: RequestInit) => {
      captured.url = String(url)
      captured.init = init
      return new Response(null, { status: 204 })
    }) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    await client.deleteHost('host-42', 'TOK')
    expect(captured.url).toBe('http://127.0.0.1:8080/hosts/host-42')
    expect(captured.init?.method).toBe('DELETE')
  })

  it('throws CpClientError on a non-2xx delete', async () => {
    const fetchFn = (async () => new Response('no', { status: 404 })) as typeof fetch
    const client = createCpClient({ cpUrl: 'http://127.0.0.1:8080', fetchFn })
    await expect(client.deleteHost('h', 't')).rejects.toMatchObject({ status: 404 })
  })
})
