import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import {BatchHelper} from '../BatchHelper';
import { BX24Dev } from '../BX24Dev';
import {batchCmdElement} from '../types/batchElement';
import authExample from './constants/authExample';
import auth from './mocks/auth';
import batch from './mocks/batch';
import batchAppOption from './mocks/batchAppOption';
import leadList from './mocks/leadList';

let mock:MockAdapter;
let bx24:BX24Dev;

beforeAll(async () => {
    mock = new MockAdapter(axios);
    mock.onPost('https://oauth.bitrix.info/oauth/token/').reply(201, auth);
    mock.onPost('https://example.bitrix24.ru/rest/batch.json').reply(200, batchAppOption);
    await new Promise(resolve=>{
        bx24=new BX24Dev(authExample, ()=>{
            resolve(true);
        });
    });
    mock.reset();
});

afterEach(() => {
    mock.reset();
});

test('batchHelper test 50', async () => {
    const testBatch:batchCmdElement={};
    for (let idx=0; idx<50; idx++){
        testBatch[`test_${idx}`]=['method', {params:true}];
    }

    const helper=new BatchHelper();
    const arrBatch=helper.addToBatch(testBatch).getArrBatches();
    expect(Array.isArray(arrBatch)).toBe(true);
    expect(JSON.stringify(arrBatch)).toBe(JSON.stringify([testBatch]));
});

test('batchHelper test 100', async () => {
    const helper=new BatchHelper();
    const testBatch:batchCmdElement={};
    for (let idx=0; idx<50; idx++){
        testBatch[`test_${idx}`]=['method', {params:true}];
    }
    helper.addToBatch(testBatch);
    const testBatch2:batchCmdElement={};
    for (let idx=0; idx<50; idx++){
        testBatch2[`test2_${idx}`]=['method', {params:true}];
    }
    const arrBatch=helper.addToBatch(testBatch2).getArrBatches();
    expect(Array.isArray(arrBatch)).toBe(true);
    expect(JSON.stringify(arrBatch)).toBe(JSON.stringify([testBatch, testBatch2]));
});

test('batchHelper test 3', async () => {
    const helper=new BatchHelper();
    helper.addToBatch({test_1:['method', {params:true}]});
    helper.addToBatch({test_2:['method', {params:true}]});
    helper.addToBatch({test_3:['method', {params:true}]});
    const arrBatch=helper.getArrBatches();

    const result=[{
        test_1:['method', {params:true}],
        test_2:['method', {params:true}],
        test_3:['method', {params:true}]
    }];

    expect(Array.isArray(arrBatch)).toBe(true);
    expect(JSON.stringify(arrBatch)).toBe(JSON.stringify(result));
});

test('real test', async ()=>{
    mock.onPost('https://example.bitrix24.ru/rest/batch.json').reply(200, batch);
    const helper=new BatchHelper();
    helper.addToBatch({getDeals:['crm.deal.list', {}]});
    helper.addToBatch({getLeads:['crm.lead.list', {}]});
    helper.addToBatch({getByID:['crm.deal.get', {id:123123}]});

    const batchRaw=helper.getArrBatches()[0];

    const result=await bx24.callBatch(batchRaw);
    for (const request in result){
        switch (request) {
            case 'getDeals':
                expect(result[request].data()).toEqual(batch.result.result.getDeals);
                expect(result[request].total()).toEqual(batch.result.result_total.getDeals);
                expect(result[request].error()).toBeUndefined();
                break;
            case 'getLeads':
                expect(result[request].data()).toEqual(leadList.result);
                expect(result[request].total()).toEqual(leadList.total);
                expect(result[request].error()).toBeUndefined();
                break;
            case 'getByID':{
                expect(result[request].error()).toEqual({
                    status: 200,
                    ex: { error: '', error_description: 'Not found' }
                });
                break;
            }
            default:
                break;
        }
    }
});

describe('getArrBatches()', () => {
    test('должен разбить объект на чанки без зависимостей', () => {
        const batchHelper=new BatchHelper();
        batchHelper.addToBatch({
            getLead: ['crm.lead.list', { filter: { id: 1 } }],
            getContact: ['crm.contact.list', { filter: { id: 2 } }],
            getCompany: ['crm.company.list', { filter: { id: 3 } }],
            getLead_2: ['crm.lead.list', { filter: { id: 4 } }],
            getContact_2: ['crm.contact.list', { filter: { id: 5 } }],
        });

        const chunkSize = 2;
        const expectedChunks = [
            {
                getLead: ['crm.lead.list', { filter: { id: 1 } }],
                getContact: ['crm.contact.list', { filter: { id: 2 } }],
            },
            {
                getCompany: ['crm.company.list', { filter: { id: 3 } }],
                getLead_2: ['crm.lead.list', { filter: { id: 4 } }],
            },
            {
                getContact_2: ['crm.contact.list', { filter: { id: 5 } }],
            },
        ];

        const result = batchHelper.getArrBatches(chunkSize);
        expect(result).toEqual(expectedChunks);
    });

    test('должен учитывать зависимости в чанках', () => {
        const batchHelper=new BatchHelper();
        batchHelper.addToBatch({
            getLead: ['crm.lead.list', { filter: { id: 1 } }],
            getContact: ['crm.contact.list', { filter: { id: 2 } }],
            getCompany: ['crm.company.list', { filter: { id: 3 } }],
            getContactByLead: ['crm.contact.list', { filter: { id: '$result[getLead][0][CONTACT_ID]' } }],
            getCompanyByLead: ['crm.company.list', { filter: { id: '$result[getLead][0][COMPANY_ID]' } }],
        });

        const chunkSize = 3;
        const expectedChunks = [
            {
                getLead: ['crm.lead.list', { filter: { id: 1 } }],
                getContactByLead: ['crm.contact.list', { filter: { id: '$result[getLead][0][CONTACT_ID]' } }],
                getCompanyByLead: ['crm.company.list', { filter: { id: '$result[getLead][0][COMPANY_ID]' } }],
            },
            {
                getContact: ['crm.contact.list', { filter: { id: 2 } }],
                getCompany: ['crm.company.list', { filter: { id: 3 } }],
            },
        ];

        const result = batchHelper.getArrBatches(chunkSize);
        expect(result).toEqual(expectedChunks);
    });

    test('должен учитывать зависимости и оставлять пакеты в одном чанке', () => {
        const batchHelper=new BatchHelper();
        batchHelper.addToBatch({
            getLead: ['crm.lead.list', { filter: { id: 1 } }],
            getContact: ['crm.contact.list', { filter: { id: 2 } }],
            getCompany: ['crm.company.list', { filter: { id: 3 } }],
            getContactByLead: ['crm.contact.list', { filter: { id: '$result[getLead][0][CONTACT_ID]' } }],
            getCompanyByLead: ['crm.company.list', { filter: { id: '$result[getLead][0][COMPANY_ID]' } }],
            getDeal: ['crm.deal.list', { filter: { COMPANY_ID: '$result[getCompany][0][ID]' } }],
        });

        const chunkSize = 4;
        const expectedChunks = [
            {
                getLead: ['crm.lead.list', { filter: { id: 1 } }],
                getContactByLead: ['crm.contact.list', { filter: { id: '$result[getLead][0][CONTACT_ID]' } }],
                getCompanyByLead: ['crm.company.list', { filter: { id: '$result[getLead][0][COMPANY_ID]' } }],
                getContact: ['crm.contact.list', { filter: { id: 2 } }],
            },
            {
                getCompany: ['crm.company.list', { filter: { id: 3 } }],
                getDeal: ['crm.deal.list', { filter: { COMPANY_ID: '$result[getCompany][0][ID]' } }],
            },
        ];

        const result = batchHelper.getArrBatches(chunkSize);
        expect(result).toEqual(expectedChunks);
    });

    test('должен корректно разбить объект на чанки с одним запросом на чанк', () => {
        const batchHelper=new BatchHelper();
        batchHelper.addToBatch({
            getLead: ['crm.lead.list', { filter: { id: 1 } }],
            getContact: ['crm.contact.list', { filter: { id: 2 } }],
            getCompany: ['crm.company.list', { filter: { id: 3 } }],
        });

        const chunkSize = 1;
        const expectedChunks = [
            {
                getLead: ['crm.lead.list', { filter: { id: 1 } }],
            },
            {
                getContact: ['crm.contact.list', { filter: { id: 2 } }],
            },
            {
                getCompany: ['crm.company.list', { filter: { id: 3 } }],
            },
        ];

        const result = batchHelper.getArrBatches(chunkSize);
        expect(result).toEqual(expectedChunks);
    });
});
