import { describe, expect, it } from 'vitest'
import { AppProblem, ApiProblem, asProblemDetails, toAppProblem } from '../src/api/contracts'

describe('AppProblem', () => {
  it('preserves structured conflict recovery metadata', () => {
    const problem = new AppProblem(409, {
      code: 'conflict',
      detail: 'The resource changed.',
      traceId: 'trace-1',
      operationId: 'operation-1',
      currentRevision: 7,
    }, 'fallback')

    expect(problem.kind).toBe('conflict')
    expect(problem.recoveryAction).toBe('reload-conflict')
    expect(problem.traceId).toBe('trace-1')
    expect(problem.operationId).toBe('operation-1')
    expect(problem.currentRevision).toBe(7)
  })

  it('maps network failures to a recoverable offline state', () => {
    const problem = toAppProblem(new TypeError('Failed to fetch'))

    expect(problem.kind).toBe('offline')
    expect(problem.recoveryAction).toBe('inspect-connection')
  })

  it('does not turn missing conflict metadata into revision zero', () => {
    const problem = new AppProblem(409, { code: 'conflict' }, 'fallback')

    expect(problem.currentRevision).toBeNull()
    expect(problem.retryAfterSeconds).toBeNull()
  })

  it('keeps the legacy ApiProblem compatible with the common contract', () => {
    const problem = new ApiProblem(401, { detail: 'Session expired.' }, 'fallback')

    expect(problem).toBeInstanceOf(AppProblem)
    expect(problem.kind).toBe('authentication')
    expect(problem.recoveryAction).toBe('reauthenticate')
  })

  it('normalizes a legacy message payload at the frontend boundary', () => {
    const problem = asProblemDetails({ message: 'Gateway unavailable.' }, 503)

    expect(problem?.detail).toBe('Gateway unavailable.')
    expect(problem?.status).toBe(503)
  })

  it('normalizes a legacy error payload without replacing structured details', () => {
    expect(asProblemDetails({ error: 'Legacy error.' }, 400)?.detail).toBe('Legacy error.')
    expect(asProblemDetails({ detail: 'Canonical.', error: 'Legacy.' }, 409)?.detail).toBe('Canonical.')
  })

  it.each([
    [401, 'authentication', 'reauthenticate'],
    [403, 'permission', 'none'],
    [409, 'conflict', 'reload-conflict'],
    [429, 'rate-limit', 'retry'],
    [503, 'offline', 'inspect-connection'],
    [504, 'timeout', 'retry'],
  ] as const)(
    'maps HTTP %s to the %s recovery presentation',
    (status, kind, recoveryAction) => {
      const problem = new AppProblem(status, null, `HTTP ${status}`)

      expect(problem.kind).toBe(kind)
      expect(problem.recoveryAction).toBe(recoveryAction)
    },
  )
})
