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

import {
  listHelcimCustomerTransactions,
  updateHelcimCustomerDefaultPaymentMethod,
  updateHelcimSubscriptionPaymentMethod
} from '../../../server/utils/helcim.js'

const mockFetch = vi.fn()
vi.stubGlobal('fetch', mockFetch)

function okResponse(payload) {
  return {
    ok: true,
    status: 200,
    text: async () => (payload === null || payload === undefined ? '' : JSON.stringify(payload))
  }
}

function errResponse(status = 500, body = 'boom') {
  return {
    ok: false,
    status,
    text: async () => body
  }
}

describe('listHelcimCustomerTransactions', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })
  afterEach(() => {
    mockFetch.mockReset()
  })

  it('normalizes, sorts newest-first, and maps statuses', async () => {
    mockFetch.mockResolvedValue(okResponse([
      {
        transactionId: 1,
        dateCreated: '2024-01-10 10:00:00',
        amount: 10,
        currency: 'CAD',
        status: 'APPROVED',
        type: 'purchase'
      },
      {
        transactionId: 2,
        dateCreated: '2024-03-15 10:00:00',
        amount: 25,
        currency: 'CAD',
        status: 'APPROVED',
        type: 'refund'
      },
      {
        transactionId: 3,
        dateCreated: '2024-02-01 10:00:00',
        amount: 5,
        currency: 'CAD',
        status: 'DECLINED',
        type: 'purchase'
      },
      {
        transactionId: 4,
        dateCreated: '2024-02-15 10:00:00',
        amount: 1,
        currency: 'CAD',
        status: 'UNKNOWN',
        type: 'purchase'
      }
    ]))

    const result = await listHelcimCustomerTransactions('CST1044')

    expect(mockFetch).toHaveBeenCalledTimes(1)
    const url = mockFetch.mock.calls[0][0]
    expect(url).toContain('/card-transactions/')
    expect(url).toContain('customerCode=CST1044')
    expect(url).toContain('limit=50')

    expect(result).toEqual([
      { id: '2', date: '2024-03-15 10:00:00', amount: 25, status: 'refunded', currency: 'CAD' },
      { id: '4', date: '2024-02-15 10:00:00', amount: 1, status: 'other', currency: 'CAD' },
      { id: '3', date: '2024-02-01 10:00:00', amount: 5, status: 'failed', currency: 'CAD' },
      { id: '1', date: '2024-01-10 10:00:00', amount: 10, status: 'paid', currency: 'CAD' }
    ])
  })

  it('throws when Helcim returns non-2xx', async () => {
    mockFetch.mockResolvedValue(errResponse(500))

    await expect(listHelcimCustomerTransactions('CST1044')).rejects.toMatchObject({
      statusCode: 500,
      statusMessage: 'Transaction lookup failed'
    })
  })
})

describe('updateHelcimCustomerDefaultPaymentMethod', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })
  afterEach(() => {
    mockFetch.mockReset()
  })

  it('resolves cardToken to cardId then PATCHes default endpoint', async () => {
    // First call: list cards filtered by token
    mockFetch.mockResolvedValueOnce(okResponse([
      { id: 161462, cardToken: 'tok-abc', cardHolderName: 'Jane' }
    ]))
    // Second call: PATCH default
    mockFetch.mockResolvedValueOnce(okResponse({ id: 2488717, customerCode: 'CST1200' }))

    const result = await updateHelcimCustomerDefaultPaymentMethod('2488717', 'tok-abc')

    expect(mockFetch).toHaveBeenCalledTimes(2)
    const firstUrl = mockFetch.mock.calls[0][0]
    expect(firstUrl).toContain('/customers/2488717/cards')
    expect(firstUrl).toContain('cardToken=tok-abc')

    const secondUrl = mockFetch.mock.calls[1][0]
    const secondOpts = mockFetch.mock.calls[1][1]
    expect(secondUrl).toContain('/customers/2488717/cards/161462/default')
    expect(secondOpts.method).toBe('PATCH')

    expect(result).toEqual({ id: 2488717, customerCode: 'CST1200' })
  })

  it('throws 404 when cardToken is not attached to the customer', async () => {
    mockFetch.mockResolvedValueOnce(okResponse([]))

    await expect(
      updateHelcimCustomerDefaultPaymentMethod('2488717', 'nope')
    ).rejects.toMatchObject({
      statusCode: 404,
      statusMessage: 'Card token not found on customer'
    })

    // Only the lookup should have happened — no PATCH
    expect(mockFetch).toHaveBeenCalledTimes(1)
  })

  it('propagates Helcim errors from the PATCH call', async () => {
    mockFetch.mockResolvedValueOnce(okResponse([
      { id: 161462, cardToken: 'tok-abc' }
    ]))
    mockFetch.mockResolvedValueOnce(errResponse(500))

    await expect(
      updateHelcimCustomerDefaultPaymentMethod('2488717', 'tok-abc')
    ).rejects.toMatchObject({
      statusCode: 500,
      statusMessage: 'Default card update failed'
    })
  })
})

describe('updateHelcimSubscriptionPaymentMethod', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })
  afterEach(() => {
    mockFetch.mockReset()
  })

  it('PATCHes /subscriptions with cardToken wrapped in subscriptions array', async () => {
    mockFetch.mockResolvedValue(okResponse({ data: [{ id: 123456 }] }))

    const result = await updateHelcimSubscriptionPaymentMethod('123456', 'tok-abc')

    expect(mockFetch).toHaveBeenCalledTimes(1)
    const [url, opts] = mockFetch.mock.calls[0]
    expect(url).toContain('/subscriptions')
    expect(opts.method).toBe('PATCH')

    const body = JSON.parse(opts.body)
    expect(body).toEqual({
      subscriptions: [{ id: 123456, cardToken: 'tok-abc' }]
    })

    expect(result).toEqual({ data: [{ id: 123456 }] })
  })

  it('throws when Helcim returns non-2xx', async () => {
    mockFetch.mockResolvedValue(errResponse(400))

    await expect(
      updateHelcimSubscriptionPaymentMethod('123456', 'tok-abc')
    ).rejects.toMatchObject({
      statusCode: 400,
      statusMessage: 'Subscription update failed'
    })
  })
})
