import { describe, it, expect } from 'vitest'
import { mapHttpStatusToErrorType, createHttpError } from '../http-error-mapper'

describe('mapHttpStatusToErrorType', () => {
  describe('credentials_invalid', () => {
    it('returns credentials_invalid for 401', () => {
      expect(mapHttpStatusToErrorType(401)).toBe('credentials_invalid')
    })

    it('returns credentials_invalid for 403', () => {
      expect(mapHttpStatusToErrorType(403)).toBe('credentials_invalid')
    })
  })

  describe('validation_error', () => {
    it('returns validation_error for 400', () => {
      expect(mapHttpStatusToErrorType(400)).toBe('validation_error')
    })

    it('returns validation_error for 404', () => {
      expect(mapHttpStatusToErrorType(404)).toBe('validation_error')
    })

    it('returns validation_error for 409', () => {
      expect(mapHttpStatusToErrorType(409)).toBe('validation_error')
    })

    it('returns validation_error for other 4xx codes', () => {
      expect(mapHttpStatusToErrorType(422)).toBe('validation_error')
      expect(mapHttpStatusToErrorType(405)).toBe('validation_error')
      expect(mapHttpStatusToErrorType(410)).toBe('validation_error')
    })
  })

  describe('rate_limit_exceeded', () => {
    it('returns rate_limit_exceeded for 429', () => {
      expect(mapHttpStatusToErrorType(429)).toBe('rate_limit_exceeded')
    })
  })

  describe('server_unavailable', () => {
    it('returns server_unavailable for 500', () => {
      expect(mapHttpStatusToErrorType(500)).toBe('server_unavailable')
    })

    it('returns server_unavailable for 502', () => {
      expect(mapHttpStatusToErrorType(502)).toBe('server_unavailable')
    })

    it('returns server_unavailable for 503', () => {
      expect(mapHttpStatusToErrorType(503)).toBe('server_unavailable')
    })

    it('returns server_unavailable for 504', () => {
      expect(mapHttpStatusToErrorType(504)).toBe('server_unavailable')
    })
  })

  describe('api_error', () => {
    it('returns api_error for unexpected status codes', () => {
      expect(mapHttpStatusToErrorType(200)).toBe('api_error')
      expect(mapHttpStatusToErrorType(301)).toBe('api_error')
    })
  })
})

describe('createHttpError', () => {
  const createMockResponse = (status: number, body: string, statusText = 'Error'): Response => {
    return {
      ok: false,
      status,
      statusText,
      text: async () => body,
    } as Response
  }

  it('creates error with correct type based on status code', async () => {
    const response = createMockResponse(401, '{}')
    const error = await createHttpError(response, {
      integration: 'test',
      method: 'testMethod'
    })

    expect(error.errorType).toBe('credentials_invalid')
  })

  it('parses JSON error message', async () => {
    const response = createMockResponse(400, JSON.stringify({ message: 'Invalid input' }))
    const error = await createHttpError(response, {
      integration: 'test',
      method: 'testMethod'
    })

    expect(error.message).toBe('Invalid input')
  })

  it('parses nested error.message format', async () => {
    const response = createMockResponse(400, JSON.stringify({ error: { message: 'Nested error' } }))
    const error = await createHttpError(response, {
      integration: 'test',
      method: 'testMethod'
    })

    expect(error.message).toBe('Nested error')
  })

  it('uses default message for non-JSON response', async () => {
    const response = createMockResponse(500, 'Internal Server Error', 'Internal Server Error')
    const error = await createHttpError(response, {
      integration: 'myapi',
      method: 'getData'
    })

    expect(error.message).toBe('Internal Server Error')
  })

  it('uses custom error parser when provided', async () => {
    const response = createMockResponse(400, JSON.stringify({ custom_error: 'Custom message' }))
    const error = await createHttpError(
      response,
      { integration: 'test', method: 'testMethod' },
      (body) => {
        const json = JSON.parse(body)
        return json.custom_error
      }
    )

    expect(error.message).toBe('Custom message')
  })

  it('falls back to generic parsing if custom parser fails', async () => {
    const response = createMockResponse(400, JSON.stringify({ message: 'Fallback message' }))
    const error = await createHttpError(
      response,
      { integration: 'test', method: 'testMethod' },
      () => {
        throw new Error('Parser failed')
      }
    )

    expect(error.message).toBe('Fallback message')
  })

  it('includes context in error details', async () => {
    const response = createMockResponse(404, '{}')
    const error = await createHttpError(response, {
      integration: 'attio',
      method: 'getRecord',
      recordId: 'abc123',
      organizationId: 'org456'
    })

    expect(error.details).toMatchObject({
      integration: 'attio',
      method: 'getRecord',
      recordId: 'abc123',
      organizationId: 'org456',
      statusCode: 404
    })
  })

  it('truncates very long error text', async () => {
    const longText = 'a'.repeat(1000)
    const response = createMockResponse(500, longText)
    const error = await createHttpError(response, {
      integration: 'test',
      method: 'testMethod'
    })

    // Should use default message for very long non-JSON responses
    expect(error.message).toContain('test API error: 500')
  })
})
