// SPDX-FileCopyrightText: © 2026 LEDGER SAS
// SPDX-License-Identifier: Apache-2.0

import { BadResponseError } from '@stellar/stellar-sdk'
import {
  documentationSummaryFromHorizonBody,
  getHorizonErrorBody,
  isHorizonErrorBody,
} from './horizonErrorBody'

describe('isHorizonErrorBody', () => {
  it('accepts RFC 7807 problem bodies with title and status', () => {
    expect(
      isHorizonErrorBody({
        type: 'https://stellar.org/horizon-errors/transaction_malformed',
        title: 'Transaction Malformed',
        status: 400,
        detail: 'Horizon could not decode the transaction envelope in this request.',
      })
    ).toBe(true)
  })

  it('rejects non-problem payloads', () => {
    expect(isHorizonErrorBody({ title: 'Missing status' })).toBe(false)
    expect(isHorizonErrorBody(null)).toBe(false)
  })
})

describe('getHorizonErrorBody', () => {
  const body = {
    type: 'https://stellar.org/horizon-errors/bad_request',
    title: 'Bad Request',
    status: 400,
    detail: 'Invalid cursor.',
    extras: { reason: 'cursor is invalid' },
  }

  it('reads BadResponseError.response', () => {
    const error = new BadResponseError('bad', body)
    expect(getHorizonErrorBody(error)).toEqual(body)
  })

  it('reads axios-like error.response.data', () => {
    const error = Object.assign(new Error('Request failed with status code 400'), {
      response: { data: body },
    })
    expect(getHorizonErrorBody(error)).toEqual(body)
  })
})

describe('documentationSummaryFromHorizonBody', () => {
  const genericDetail =
    'The transaction failed when submitted to the Stellar network. The `extras.result_codes` field on this response contains further details.'

  it('prefers operation result code over generic tx_failed', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Transaction Failed',
        status: 400,
        detail: genericDetail,
        extras: {
          result_codes: {
            transaction: 'tx_failed',
            operations: ['op_underfunded'],
          },
        },
      })
    ).toBe(
      'The source account does not have enough balance to send the payment amount while maintaining its minimum reserve.'
    )
  })

  it('skips op_inner and uses the first failing operation code', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Transaction Failed',
        status: 400,
        detail: genericDetail,
        extras: {
          result_codes: {
            transaction: 'tx_failed',
            operations: ['op_inner', 'op_underfunded'],
          },
        },
      })
    ).toBe(
      'The source account does not have enough balance to send the payment amount while maintaining its minimum reserve.'
    )
  })

  it('uses transaction code when it is specific', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Transaction Failed',
        status: 400,
        detail: genericDetail,
        extras: {
          result_codes: { transaction: 'tx_bad_seq' },
        },
      })
    ).toBe('Sequence number does not match source account.')
  })

  it('uses Horizon detail when no result codes are present', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Transaction Malformed',
        status: 400,
        detail: 'Horizon could not decode the transaction envelope in this request.',
        extras: { envelope_xdr: 'AAAA' },
      })
    ).toBe('Horizon could not decode the transaction envelope in this request.')
  })

  it('uses extras.reason for validation errors', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Bad Request',
        status: 400,
        extras: { invalid_field: 'cursor', reason: 'cursor is invalid' },
      })
    ).toBe('cursor is invalid (field: cursor)')
  })

  it('falls back to title when nothing else is available', () => {
    expect(
      documentationSummaryFromHorizonBody({
        title: 'Rate Limited',
        status: 429,
      })
    ).toBe('Rate Limited')
  })
})
