import { useRequest, createRequest } from '..'
import HttpRequestMock from 'http-request-mock'
const mocker = HttpRequestMock.setupForUnitTest('xhr')

const delay = (time) => new Promise((r) => setTimeout(r, time))

const getData = { data: 'hello get' }
const postData = { data: 'hello post' }
const asyncData = { data: 'async data' }

describe('useRequest', () => {
  beforeEach(() => {
    mocker.reset()
  })

  it('default', async () => {
    mocker.get('http://example.com/get', getData)

    jest.useFakeTimers()
    const shell = useRequest('http://example.com/get')
    expect(shell.isLoading).toBeFalsy()
    jest.advanceTimersByTime(50)
    expect(shell.isLoading).toBeTruthy()
    jest.useRealTimers()
    await delay(1000)

    expect(shell.isFinished).toBeTruthy()
    expect(shell.data).toEqual(getData)
  })

  it('abort', async () => {
    mocker.get('http://example.com/async', asyncData, { delay: 2000 })

    const shell = useRequest('http://example.com/async')
    await delay(50)
    expect(shell.isLoading).toBeTruthy()
    shell.abort()
    await delay(50)

    expect(shell.isAborted).toBeTruthy()
    expect(shell.isFinished).toBeTruthy()
  })

  it('immediate false', async () => {
    mocker.get('http://example.com/get', getData)

    const shell = useRequest('http://example.com/get', { immediate: false })
    await delay(500)
    expect(shell.isLoading).toBeFalsy()

    await shell.execute()
    await delay(50)

    expect(shell.isFinished).toBeTruthy()
    expect(shell.data).toEqual(getData)
  })

  it('other method: post', async () => {
    mocker.post('http://example.com/post', postData, { delay: 100 })

    const shellPost = useRequest('http://example.com/post').post()
    const shellParams = useRequest('http://example.com/post').post({ params: '1' })
    const shellType = useRequest('http://example.com/post').post({ params: '1' }, 'text')
    jest.useFakeTimers()
    jest.advanceTimersByTime(20)
    jest.useRealTimers()
    await delay(50)
    expect(shellPost.isLoading).toBeTruthy()

    await delay(100)
    expect(shellPost.isFinished).toBeTruthy()
    expect(shellPost.data).toEqual(postData)
    expect(shellParams.response?.config.data).toEqual(JSON.stringify({ params: '1' }))
    expect(shellType.response?.config.headers?.['Content-Type']).toEqual('text/plain')
  })

  it('axios config', async () => {
    mocker.get('http://example.com/async', asyncData, { delay: 200 })

    const shell = useRequest('http://example.com/async', { data: { params: '1' } })
    await delay(300)
    expect(shell.isLoading).toBeFalsy()

    expect(shell.isFinished).toBeTruthy()
    expect(shell.data).toEqual(asyncData)

    const shell2 = useRequest('http://example.com/async', { timeout: 50 })
    await delay(100)
    expect(shell2.response).toEqual(null)
    await delay(200)
    expect(shell2.response?.config.timeout).toEqual(50)
  })
})

describe('createRequest', () => {
  beforeEach(() => {
    mocker.reset()
  })
  it('baseURL', async () => {
    mocker.get('http://example.com/foo', asyncData, { delay: 100 })
    const baseURL = 'http://example.com'
    const request = createRequest({ baseURL })
    const shell = request('/foo', { immediate: false })
    await shell.execute()
    expect(shell.data).toEqual(asyncData)
  })
  it('requestIntercept', async () => {
    mocker.get('http://example.com/foo', asyncData, { delay: 100 })
    const baseURL = 'http://example.com'
    const request = createRequest({
      baseURL,
      requestIntercept: (use) => {
        use((config) => {
          config.headers = {
            ...config.headers,
            'X-CUSTOM': 'LOVE',
          }

          return config
        })
      },
    })
    const shell = request('/foo', { immediate: false })
    await shell.execute()
    expect(shell.data).toEqual(asyncData)
    expect(shell.response?.config.headers?.['X-CUSTOM']).toEqual('LOVE')
  })
  it('responseIntercept', async () => {
    mocker.get('http://example.com/foo', asyncData, { delay: 100 })
    const baseURL = 'http://example.com'
    const request = createRequest({
      baseURL,
      responseIntercept(use) {
        use((response) => {
          response.data = 'changed'
          return response
        })
      },
    })
    const shell = request('/foo', { immediate: false })
    await shell.execute()
    expect(shell.data).toEqual('changed')
  })
})
