import { beforeEach, describe, expect, it, vi } from 'vitest'

import { transactionsApi } from './transactions'

describe('transactionsApi.listByBlockSafe', () => {
  beforeEach(() => {
    vi.restoreAllMocks()
  })

  it('returns normalized transactions for a specific block', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({
        items: [
          {
            hash: '0xabc',
            block_number: 123,
            block_hash: '0xdef',
            transaction_index: 0,
            from: { hash: '0x0000000000000000000000000000000000000001' },
            to: { hash: '0x0000000000000000000000000000000000000002' },
            value: '0',
            gas_price: '1',
            gas: '21000',
            gas_used: '21000',
            status: 'ok',
            timestamp: '2026-04-16T09:40:12.000000Z',
          },
        ],
        next_page_params: { page: 2, page_size: 10 },
      }),
    })

    vi.stubGlobal('fetch', fetchMock)

    const result = await transactionsApi.listByBlockSafe(138, 123, 1, 10)

    expect(result.ok).toBe(true)
    expect(result.items).toHaveLength(1)
    expect(result.hasNextPage).toBe(true)
    expect(result.items[0]?.hash).toBe('0xabc')
    expect(fetchMock).toHaveBeenCalledTimes(1)
    expect(fetchMock.mock.calls[0]?.[0]).toEqual(
      expect.stringContaining('/api/v2/blocks/123/transactions?page=1&page_size=10'),
    )
  })

  it('returns a non-throwing failure result when the block transaction request fails', async () => {
    vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new Error('network down')))

    const result = await transactionsApi.listByBlockSafe(138, 123, 1, 10)

    expect(result).toEqual({
      ok: false,
      items: [],
      hasNextPage: false,
    })
  })
})

describe('transactionsApi.diagnoseMissing', () => {
  beforeEach(() => {
    vi.restoreAllMocks()
  })

  it('distinguishes missing explorer and missing rpc results', async () => {
    const fetchMock = vi.fn()
      .mockResolvedValueOnce({
        ok: false,
        status: 404,
        json: async () => ({ message: 'Not found' }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: null }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: null }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: '0x39fb85' }),
      })

    vi.stubGlobal('fetch', fetchMock)

    const diagnostic = await transactionsApi.diagnoseMissing(
      138,
      '0x14d7259e6e75bbcd8979dc9e19f3001b0f328bbcdb730937f8cc2e6a52f26da6'
    )

    expect(diagnostic.checked_hash).toBe('0x14d7259e6e75bbcd8979dc9e19f3001b0f328bbcdb730937f8cc2e6a52f26da6')
    expect(diagnostic.chain_id).toBe(138)
    expect(diagnostic.explorer_indexed).toBe(false)
    expect(diagnostic.rpc_transaction_found).toBe(false)
    expect(diagnostic.rpc_receipt_found).toBe(false)
    expect(diagnostic.latest_block_number).toBeTypeOf('number')
    expect(diagnostic.rpc_url).toBe('https://rpc-http-pub.d-bis.org')
  })

  it('reports when rpc can still see a transaction the explorer has not indexed', async () => {
    const fetchMock = vi.fn()
      .mockResolvedValueOnce({
        ok: false,
        status: 404,
        json: async () => ({ message: 'Not found' }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: { hash: '0xabc' } }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: null }),
      })
      .mockResolvedValueOnce({
        ok: true,
        json: async () => ({ result: '0x10' }),
      })

    vi.stubGlobal('fetch', fetchMock)

    const diagnostic = await transactionsApi.diagnoseMissing(138, '0xabc')

    expect(diagnostic.explorer_indexed).toBe(false)
    expect(diagnostic.rpc_transaction_found).toBe(true)
    expect(diagnostic.rpc_receipt_found).toBe(false)
    expect(diagnostic.latest_block_number).toBe(16)
  })
})
