/*
 * Copyright (C) 2018-2020 StApps
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */
import {expect} from 'chai';
import {Interceptable, MockAgent, setGlobalDispatcher} from 'undici';
import {HttpClient} from '../src/index.js';

const mockAgent = new MockAgent();
setGlobalDispatcher(mockAgent);

describe('HttpClient', function () {
  let mockPool: Interceptable;

  beforeEach(function () {
    mockPool?.close();
    mockPool = mockAgent.get('http://www.example.com');
  });

  it('should construct', function () {
    expect(() => {
      return new HttpClient();
    }).not.to.throw();
  });

  it('should request', async function () {
    const client = new HttpClient();

    mockPool.intercept({path: '/resource', method: 'GET'}).reply(200, {foo: 'bar'});

    const response = await client.request({
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'bar'});
  });

  it('should request with body', async function () {
    const client = new HttpClient();

    mockPool.intercept({path: '/resource', method: 'GET'}).reply(200, {foo: 'foo'});

    const response = await client.request({
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'foo'});
  });

  it('should request with error', async function () {
    const client = new HttpClient();
    let caughtError;

    mockPool.intercept({path: '/resource', method: 'GET'}).replyWithError(new Error('foo'));

    try {
      await client.request({
        body: {
          foo: 'bar',
        },
        url: new URL('http://www.example.com/resource'),
      });
    } catch (error) {
      caughtError = error;
    }

    expect(caughtError).not.to.be.undefined;
  });

  it('should request with headers', async function () {
    const client = new HttpClient();

    mockPool
      .intercept({
        path: '/resource',
        method: 'GET',
        headers: {
          'X-StApps-Version': 'foo.bar.foobar',
        },
      })
      .reply(200, {foo: 'bar'});

    const response = await client.request({
      headers: {
        'X-StApps-Version': 'foo.bar.foobar',
      },
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'bar'});
  });

  it('should request with body', async function () {
    const client = new HttpClient();

    mockPool
      .intercept({
        path: '/resource',
        method: 'POST',
        headers: {
          'content-type': 'application/json',
        },
        body: JSON.stringify({bar: 'baz'}),
      })
      .reply(200, {foo: 'foo'});

    const response = await client.request({
      body: {bar: 'baz'},
      method: 'POST',
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'foo'});
  });

  it('should request with method GET', async function () {
    const client = new HttpClient();

    mockPool.intercept({path: '/resource', method: 'GET'}).reply(200, {foo: 'foo'});

    const response = await client.request({
      method: 'GET',
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'foo'});
  });

  it('should request with method POST', async function () {
    const client = new HttpClient();

    mockPool.intercept({path: '/resource', method: 'POST'}).reply(200, {foo: 'foo'});

    const response = await client.request({
      method: 'POST',
      url: new URL('http://www.example.com/resource'),
    });

    expect(response.body).to.be.deep.equal({foo: 'foo'});
  });
});
