import { describe, expect, test } from 'bun:test'
import { SlingshotHandleResolver } from './slingshot-handle-resolver'

const PUBLIC_ADDRESS = { address: '93.184.216.34', family: 4 as const }
const ENDPOINT = 'https://resolver.example/xrpc/com.atproto.identity.resolveHandle'

function resolverFor(body: unknown, options: { addresses?: readonly { address: string; family: 4 | 6 }[] } = {}) {
	const requests: string[] = []
	const resolver = new SlingshotHandleResolver(ENDPOINT, {
		resolver: async () => options.addresses ?? [PUBLIC_ADDRESS],
		transport: async ({ url }) => {
			requests.push(url.toString())
			return new Response(JSON.stringify(body), { headers: { 'content-type': 'application/json' } })
		},
	})
	return { resolver, requests }
}

describe('SlingshotHandleResolver transport and validation', () => {
	test('pins the resolver request and accepts only supported DIDs', async () => {
		const { resolver, requests } = resolverFor({ did: 'did:web:example.com' })
		expect(await resolver.resolve('Alice.Example')).toBe('did:web:example.com')
		expect(requests).toEqual([`${ENDPOINT}?handle=alice.example`])
	})

	test('rejects malformed DID responses and unsafe DNS answers before transport', async () => {
		const malformed = resolverFor({ did: 'did:evil:private' })
		expect(await malformed.resolver.resolve('alice.example')).toBeNull()
		expect(malformed.requests).toHaveLength(1)

		const unsafe = resolverFor({ did: 'did:web:example.com' }, { addresses: [{ address: '10.0.0.1', family: 4 }] })
		expect(await unsafe.resolver.resolve('alice.example')).toBeNull()
		expect(unsafe.requests).toHaveLength(0)
	})

	test('bounds resolver responses and rejects non-HTTPS endpoints', async () => {
		const oversized = resolverFor({ did: 'did:web:example.com', padding: 'x'.repeat(64 * 1024) })
		expect(await oversized.resolver.resolve('alice.example')).toBeNull()

		const http = new SlingshotHandleResolver('http://resolver.example/xrpc/com.atproto.identity.resolveHandle', {
			resolver: async () => [PUBLIC_ADDRESS],
			transport: async () => new Response(JSON.stringify({ did: 'did:web:example.com' })),
		})
		expect(await http.resolve('alice.example')).toBeNull()
	})

	test('does not convert transient transport failures into cacheable null results', async () => {
		const resolver = new SlingshotHandleResolver(ENDPOINT, {
			resolver: async () => [PUBLIC_ADDRESS],
			transport: async () => {
				throw new Error('socket unavailable')
			},
		})
		await expect(resolver.resolve('alice.example')).rejects.toThrow('temporarily unavailable')
	})

	test('propagates caller cancellation through the pinned request', async () => {
		const controller = new AbortController()
		const resolver = new SlingshotHandleResolver(ENDPOINT, {
			resolver: async () => [PUBLIC_ADDRESS],
			transport: async () => new Promise<Response>(() => undefined),
		})
		const pending = resolver.resolve('alice.example', { signal: controller.signal })
		controller.abort()
		await expect(pending).rejects.toThrow()
	})

	test('does not send requests for invalid handles', async () => {
		const { resolver, requests } = resolverFor({ did: 'did:web:example.com' })
		expect(await resolver.resolve('not a handle')).toBeNull()
		expect(requests).toHaveLength(0)
	})
})
