import MockAdapter from 'axios-mock-adapter';

import MessengerBatch from '../MessengerBatch';
import MessengerClient from '../MessengerClient';

const ACCESS_TOKEN = 'foo_token';
const APP_SECRET = 'shhhhh!is.my.secret';

let axios;
let _create;
beforeEach(() => {
  axios = require('axios');
  _create = axios.create;
});

afterEach(() => {
  axios.create = _create;
});

describe('connect', () => {
  describe('create axios with default graphAPI version', () => {
    it('with config', () => {
      axios.create = jest.fn().mockReturnValue({
        interceptors: {
          request: {
            use: jest.fn(),
          },
        },
      });
      MessengerClient.connect({ accessToken: ACCESS_TOKEN });

      expect(axios.create).toBeCalledWith(
        expect.objectContaining({
          baseURL: 'https://graph.facebook.com/v6.0/',
          headers: { 'Content-Type': 'application/json' },
        })
      );
    });
  });

  describe('create axios with custom graphAPI version', () => {
    it('with config', () => {
      axios.create = jest.fn().mockReturnValue({
        interceptors: {
          request: {
            use: jest.fn(),
          },
        },
      });
      MessengerClient.connect({ accessToken: ACCESS_TOKEN, version: '2.6' });

      expect(axios.create).toBeCalledWith(
        expect.objectContaining({
          baseURL: 'https://graph.facebook.com/v2.6/',
          headers: { 'Content-Type': 'application/json' },
        })
      );
    });
  });

  it('support origin', () => {
    axios.create = jest.fn().mockReturnValue({
      interceptors: {
        request: {
          use: jest.fn(),
        },
      },
    });
    MessengerClient.connect({
      accessToken: ACCESS_TOKEN,
      origin: 'https://mydummytestserver.com',
    });

    expect(axios.create).toBeCalledWith(
      expect.objectContaining({
        baseURL: 'https://mydummytestserver.com/v6.0/',
        headers: { 'Content-Type': 'application/json' },
      })
    );
  });
});

describe('constructor', () => {
  describe('create axios with default graphAPI version', () => {
    it('with config', () => {
      axios.create = jest.fn().mockReturnValue({
        interceptors: {
          request: {
            use: jest.fn(),
          },
        },
      });
      new MessengerClient({ accessToken: ACCESS_TOKEN }); // eslint-disable-line no-new

      expect(axios.create).toBeCalledWith(
        expect.objectContaining({
          baseURL: 'https://graph.facebook.com/v6.0/',
          headers: { 'Content-Type': 'application/json' },
        })
      );
    });
  });

  describe('create axios with custom graphAPI version', () => {
    it('with config', () => {
      axios.create = jest.fn().mockReturnValue({
        interceptors: {
          request: {
            use: jest.fn(),
          },
        },
      });
      new MessengerClient({ accessToken: ACCESS_TOKEN, version: '2.6' }); // eslint-disable-line no-new

      expect(axios.create).toBeCalledWith(
        expect.objectContaining({
          baseURL: 'https://graph.facebook.com/v2.6/',
          headers: { 'Content-Type': 'application/json' },
        })
      );
    });
  });

  it('support origin', () => {
    axios.create = jest.fn().mockReturnValue({
      interceptors: {
        request: {
          use: jest.fn(),
        },
      },
    });
    // eslint-disable-next-line no-new
    new MessengerClient({
      accessToken: ACCESS_TOKEN,
      origin: 'https://mydummytestserver.com',
    });

    expect(axios.create).toBeCalledWith(
      expect.objectContaining({
        baseURL: 'https://mydummytestserver.com/v6.0/',
        headers: { 'Content-Type': 'application/json' },
      })
    );
  });
});

describe('#version', () => {
  it('should return version of graph api', () => {
    expect(new MessengerClient({ accessToken: ACCESS_TOKEN }).version).toEqual(
      '6.0'
    );
    expect(
      new MessengerClient({ accessToken: ACCESS_TOKEN, version: 'v2.6' })
        .version
    ).toEqual('2.6');
    expect(
      new MessengerClient({ accessToken: ACCESS_TOKEN, version: '2.6' }).version
    ).toEqual('2.6');
    expect(() => {
      // eslint-disable-next-line no-new
      new MessengerClient({ accessToken: ACCESS_TOKEN, version: 2.6 } as any);
    }).toThrow('Type of `version` must be string.');
  });
});

describe('#axios', () => {
  it('should return underlying http client', () => {
    const client = new MessengerClient({ accessToken: ACCESS_TOKEN });

    expect(client.axios.get).toBeDefined();
    expect(client.axios.post).toBeDefined();
    expect(client.axios.put).toBeDefined();
    expect(client.axios.delete).toBeDefined();
  });
});

describe('#accessToken', () => {
  it('should return underlying access token', () => {
    const client = new MessengerClient({ accessToken: ACCESS_TOKEN });

    expect(client.accessToken).toBe(ACCESS_TOKEN);
  });
});

describe('#appSecret', () => {
  it('should return underlying appSecret', () => {
    const client = new MessengerClient({
      accessToken: ACCESS_TOKEN,
      appSecret: APP_SECRET,
    });

    expect(client.appSecret).toEqual(APP_SECRET);
  });
});

describe('#onRequest', () => {
  it('should call onRequest when calling any API', async () => {
    const onRequest = jest.fn();
    const client = new MessengerClient({
      accessToken: ACCESS_TOKEN,
      onRequest,
    });

    const mock = new MockAdapter(client.axios);

    mock.onPost('/path').reply(200, {});

    await client.axios.post('/path', { x: 1 });

    expect(onRequest).toBeCalledWith({
      method: 'post',
      url: 'https://graph.facebook.com/v6.0/path',
      body: {
        x: 1,
      },
      headers: {
        'Content-Type': 'application/json',
        Accept: 'application/json, text/plain, */*',
      },
    });
  });
});

describe('appsecret proof', () => {
  it('should add appsecret proof to requests if appSecret exists', async () => {
    expect.assertions(1);

    const client = new MessengerClient({
      accessToken: ACCESS_TOKEN,
      appSecret: APP_SECRET,
    });

    const mock = new MockAdapter(client.axios);

    const USER_ID = 'USER_ID';

    const reply = {
      recipient_id: USER_ID,
      message_id: 'mid.1489394984387:3dd22de509',
    };

    let url;
    mock.onPost().reply((config) => {
      url = config.url;
      return [200, reply];
    });

    await client.sendText(USER_ID, 'Hello!');

    expect(url).toEqual(
      '/me/messages?access_token=foo_token&appsecret_proof=796ba0d8a6b339e476a7b166a9e8ac0a395f7de736dc37de5f2f4397f5854eb8'
    );
  });

  it('should not add appsecret proof to requests if skipAppSecretProof: true', async () => {
    expect.assertions(1);

    const client = new MessengerClient({
      accessToken: ACCESS_TOKEN,
      appSecret: APP_SECRET,
      skipAppSecretProof: true,
    });

    const mock = new MockAdapter(client.axios);

    const USER_ID = 'USER_ID';

    const reply = {
      recipient_id: USER_ID,
      message_id: 'mid.1489394984387:3dd22de509',
    };

    let url;
    mock.onPost().reply((config) => {
      url = config.url;
      return [200, reply];
    });

    await client.sendText(USER_ID, 'Hello!');

    expect(url).toEqual('/me/messages?access_token=foo_token');
  });

  it('should add appsecret proof to batch requests if appSecret exists', async () => {
    expect.assertions(2);

    const client = new MessengerClient({
      accessToken: ACCESS_TOKEN,
      appSecret: APP_SECRET,
    });

    const mock = new MockAdapter(client.axios);

    const USER_ID = 'USER_ID';

    const reply = [
      {
        recipient_id: USER_ID,
        message_id: 'mid.1489394984387:3dd22de509',
      },
      {
        id: USER_ID,
        first_name: 'Kevin',
        last_name: 'Durant',
        profile_pic: 'https://example.com/pic.png',
      },
    ];

    let url;
    let data;
    mock.onPost().reply((config) => {
      url = config.url;
      data = config.data;
      return [200, reply];
    });

    const batch = [
      MessengerBatch.sendText(USER_ID, 'Hello', { accessToken: 'token1' }),
      MessengerBatch.getUserProfile(USER_ID, { accessToken: 'token2' }),
    ];

    await client.sendBatch(batch);

    expect(url).toEqual(
      '/?appsecret_proof=796ba0d8a6b339e476a7b166a9e8ac0a395f7de736dc37de5f2f4397f5854eb8'
    );
    expect(JSON.parse(data)).toEqual({
      access_token: ACCESS_TOKEN,
      include_headers: true,
      batch: [
        {
          method: 'POST',
          relative_url:
            'me/messages?appsecret_proof=99eed1703c01487bce54ccf12b7e8007880e3bf7f3820656f17f200b5c976266',
          body: `messaging_type=UPDATE&recipient=%7B%22id%22%3A%22${USER_ID}%22%7D&message=%7B%22text%22%3A%22Hello%22%7D&access_token=token1`,
        },
        {
          method: 'GET',
          relative_url:
            'USER_ID?fields=id%2Cname%2Cfirst_name%2Clast_name%2Cprofile_pic&access_token=token2&appsecret_proof=f1405d923148b8e76e002138a7adddfec6ce4075712095d88b4b4b6777ad45e5',
        },
      ],
    });
  });
});
