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

import { RevolutionaryTypedFetch } from '../src/core/typed-fetch.js'

describe('mock controller integration', () => {
  afterEach(() => {
    vi.restoreAllMocks()
  })

  it('intercepts matching requests and skips fetch', async () => {
    const fetchSpy = vi.fn(() => Promise.reject(new Error('network should not be called')))
    vi.stubGlobal('fetch', fetchSpy)

    const client = new RevolutionaryTypedFetch({ request: { baseURL: 'https://api.test' } })
    client.mock({
      method: 'GET',
      url: '/users/:id',
      response: {
        data: { id: '123', name: 'Mock User' },
        status: 200
      }
    })

    const result = await client.get('/users/123')
    expect(result.data).toEqual({ id: '123', name: 'Mock User' })
    expect(result.response.status).toBe(200)
    expect(fetchSpy).not.toHaveBeenCalled()
  })

  it('passes params, query, headers, and body to handlers', async () => {
    const fetchSpy = vi.fn(() => Promise.reject(new Error('network should not be called')))
    vi.stubGlobal('fetch', fetchSpy)

    const client = new RevolutionaryTypedFetch({ request: { baseURL: 'https://api.test' } })
    client.mock({
      method: 'PATCH',
      url: '/users/:id',
      handler: ({ params, query, headers, body }) => {
        expect(params.id).toBe('42')
        expect(query.filter).toBe('active')
        expect(headers['x-test']).toBe('1')
        expect((body as any).title).toBe('Director')
        return {
          data: {
            id: params.id,
            filter: query.filter,
            seenHeader: headers['x-test']
          }
        }
      }
    })

    const { data } = await client.patch(
      '/users/42?filter=active',
      { title: 'Director' },
      { headers: { 'X-Test': '1' } }
    )

    expect(data).toEqual({ id: '42', filter: 'active', seenHeader: '1' })
    expect(fetchSpy).not.toHaveBeenCalled()
  })

  it('mockOnce only intercepts the first call', async () => {
    const fetchSpy = vi.fn(async () =>
      new Response(JSON.stringify({ ok: true }), {
        headers: { 'Content-Type': 'application/json' }
      })
    )
    vi.stubGlobal('fetch', fetchSpy)

    const client = new RevolutionaryTypedFetch({ request: { baseURL: 'https://api.test' } })
    client.mockOnce({
      method: 'GET',
      url: '/ping',
      response: { data: { mocked: true } }
    })

    const first = await client.get('/ping')
    expect(first.data).toEqual({ mocked: true })

    const second = await client.get('/ping')
    expect(fetchSpy).toHaveBeenCalledTimes(1)
    expect(second.data).toEqual({ ok: true })
  })

  it('clearMocks removes registered routes', async () => {
    const fetchSpy = vi.fn(async () =>
      new Response(JSON.stringify({ ok: true }), {
        headers: { 'Content-Type': 'application/json' }
      })
    )
    vi.stubGlobal('fetch', fetchSpy)

    const client = new RevolutionaryTypedFetch({ request: { baseURL: 'https://api.test' } })
    client.mock({
      method: 'GET',
      url: '/users',
      response: { data: [{ id: 1 }] }
    })

    client.clearMocks()

    const result = await client.get('/users')
    expect(result.data).toEqual({ ok: true })
    expect(fetchSpy).toHaveBeenCalledTimes(1)
  })
})
