import { describe, expect, it } from 'vitest';
import { BackendQueryFormat, LogicNodeAttributes, MathFilters, NumberFunctions, QueryElementTypes } from '../model';
import { QueryMultiGraphology } from '../model/graphology/utils';
import { NumberAggregationTypes, NumberFilterTypes, NumberFunctionTypes } from '../model/logic/general';
import { Query2BackendQuery, calculateQueryLogic } from './query2backend';
import { SerializedNode } from 'graphology-types';
import { MathAggregations } from '../model/logic/numberAggregations';
import { QueryBuilderSettings } from '../../data-access/store/querybuilderSlice';

const defaultQuery = {
  saveStateID: 'database',
  cached: false,
  query: [],
  limit: 500,
  return: ['*'],
  machineLearning: [],
};

const defaultSettings: QueryBuilderSettings = {
  limit: 500,
  depth: { min: 0, max: 1 },
  layout: 'manual',
  autocompleteRelation: true,
  unionTypes: {},
};

describe('QueryUtils Entity and Relations', () => {
  it('should create an instance', () => {
    const graph = new QueryMultiGraphology();
    expect(true).toBeTruthy();
  });

  it('should run simple query translation', () => {
    const graph = new QueryMultiGraphology();

    const entity1 = graph.addPill2Graphology({
      id: '0',
      type: QueryElementTypes.Entity,
      x: 100,
      y: 100,
      name: 'Airport 1',
      attributes: [],
    });
    const entity2 = graph.addPill2Graphology({
      id: '10',
      type: QueryElementTypes.Entity,
      x: 200,
      y: 200,
      name: 'Airport 2',
      attributes: [],
    });

    const relation1 = graph.addPill2Graphology({
      id: '1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(entity1, relation1);
    graph.addEdge2Graphology(relation1, entity2);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: '0',
            label: 'Airport 1',
            relation: {
              id: '1',
              label: 'Flight between airports',
              direction: 'TO',
              node: {
                id: '10',
                label: 'Airport 2',
                relation: undefined,
              },
            },
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run multiple path query translation', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1, { type: 'connection' });
    graph.addEdge2Graphology(r1, e2, { type: 'connection' });

    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });

    const r12 = graph.addPill2Graphology({
      id: 'r12',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports 2',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e12, r12);
    graph.addEdge2Graphology(r12, e22);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e0',
            label: 'Airport 1',
            relation: {
              id: 'r1',
              label: 'Flight between airports',
              direction: 'TO',
              node: {
                id: 'e1',
                label: 'Airport 2',
                relation: undefined,
              },
            },
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e12',
            label: 'Airport 12',
            relation: {
              id: 'r12',
              label: 'Flight between airports 2',
              direction: 'TO',
              node: {
                id: 'e22',
                label: 'Airport 22',
                relation: undefined,
              },
            },
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run one relation multiple entity query translation', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1);
    graph.addEdge2Graphology(r1, e2);

    graph.addEdge2Graphology(e12, r1);
    graph.addEdge2Graphology(r1, e22);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } },
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e22', label: 'Airport 22' } },
          },
        },
        {
          id: 'path_2',
          node: {
            id: 'e12',
            label: 'Airport 12',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } },
          },
        },
        {
          id: 'path_3',
          node: {
            id: 'e12',
            label: 'Airport 12',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e22', label: 'Airport 22' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run lone entities query translation', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });
    const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] });
    const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1);
    graph.addEdge2Graphology(r1, e2);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e0',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 2' } },
          },
        },
        { id: 'path_1', node: { id: 'e12', label: 'Airport 12' } },
        { id: 'path_2', node: { id: 'e22', label: 'Airport 22' } },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run lone relations query translation', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });
    const r2 = graph.addPill2Graphology({
      id: 'r2',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports 2',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1);
    graph.addEdge2Graphology(r1, e2);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e0',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 2' } },
          },
        },
        {
          id: 'path_1',
          node: { relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run relation only left side connected query translation', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e0',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: {} },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run relation only right side connected query translation', () => {
    const graph = new QueryMultiGraphology();

    const e2 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(r1, e2);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e0', label: 'Airport 2' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run entity and relations multi connection', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    const r2 = graph.addPill2Graphology({
      id: 'r2',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports 2',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1);
    graph.addEdge2Graphology(e1, r2);
    graph.addEdge2Graphology(r1, e2);
    graph.addEdge2Graphology(r2, e2);

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } },
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of loops', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1, { type: 'connection' });
    graph.addEdge2Graphology(r1, e1, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of loops and regular', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1, { type: 'connection' });
    graph.addEdge2Graphology(r1, e1, { type: 'connection' });
    graph.addEdge2Graphology(r1, e2, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: {
              id: 'r1',
              label: 'Flight between airports',
              direction: 'TO',
              node: { id: 'e2', label: 'Airport 1', relation: undefined },
            },
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of loops and regular left', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(e1, r1, { type: 'connection' });
    graph.addEdge2Graphology(e2, r1, { type: 'connection' });
    graph.addEdge2Graphology(r1, e1, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } },
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e2',
            label: 'Airport 1',
            relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of direct entity connection', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    graph.addEdge2Graphology(e1, e2, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: { direction: 'BOTH', node: { id: 'e2', label: 'Airport 1' } },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of direct relation connection', () => {
    const graph = new QueryMultiGraphology();

    const r1 = graph.addPill2Graphology({
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    const r2 = graph.addPill2Graphology({
      id: 'r2',
      type: QueryElementTypes.Relation,
      x: 140,
      y: 140,
      name: 'Flight between airports 2',
      collection: 'Relation Pill',
      depth: { min: 0, max: 1 },
      attributes: [],
    });

    graph.addEdge2Graphology(r1, r2, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            relation: {
              id: 'r1',
              label: 'Flight between airports',
              direction: 'TO',
              node: { relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } },
            },
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of entity only loop', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });
    const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] });

    graph.addEdge2Graphology(e1, e2, { type: 'connection' });
    graph.addEdge2Graphology(e2, e1, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: {
              direction: 'BOTH',
              node: {
                id: 'e2', label: 'Airport 2',
                relation: {
                  direction: 'BOTH',
                  node: { id: 'e1', label: 'Airport 1' }
                },
              }
            },
          },
        }
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run in case of entity only self loop', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] });

    graph.addEdge2Graphology(e1, e1, { type: 'connection' });

    const expected = {
      ...defaultQuery,
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1', label: 'Airport 1',
            relation: {
              direction: 'BOTH',
              node: { id: 'e1', label: 'Airport 1' }
            }
          },
        },
      ],
    };
    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });
});

describe('QueryUtils calculateQueryLogic', () => {
  it('should run simple logic', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology(
      {
        id: 'e1',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 1',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const l1 = graph.addLogicPill2Graphology({
      id: 'l1',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Logic 1',
      logic: MathFilters[NumberFilterTypes.EQUAL],
      attributes: [],
      inputs: {},
    });

    graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' });
    const graphExport = graph.export();
    let logics = graphExport.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[];

    const ret = calculateQueryLogic(logics[0], graphExport, logics);
  });
});

describe('QueryUtils with Logic', () => {
  it('should run simple query with logic', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology(
      {
        id: 'e1',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 1',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const l1 = graph.addLogicPill2Graphology({
      id: 'l1',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Logic 1',
      logic: MathFilters[NumberFilterTypes.EQUAL],
      attributes: [],
      inputs: {},
    });

    graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });

    const expected: BackendQueryFormat = {
      ...defaultQuery,
      logic: ['==', '@e1.age', 0],
      machineLearning: [],
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: undefined,
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should run add age and filter', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology(
      {
        id: 'e1',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 1',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const e2 = graph.addPill2Graphology(
      {
        id: 'e2',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 2',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const l1 = graph.addLogicPill2Graphology({
      id: 'l1',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Filter EQ',
      logic: MathFilters[NumberFilterTypes.EQUAL],
      attributes: [],
      inputs: {},
    });

    const l2 = graph.addLogicPill2Graphology({
      id: 'l2',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Logic ADD',
      logic: NumberFunctions[NumberFunctionTypes.ADD],
      attributes: [],
      inputs: {},
    });

    graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });
    graph.addEdge2Graphology(e2, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' });
    graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: NumberFilterTypes.EQUAL, targetHandleName: 'Value' });

    const expected: BackendQueryFormat = {
      ...defaultQuery,
      logic: ['==', ['+', '@e1.age', '@e2.age'], 0],
      machineLearning: [],
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: undefined,
          },
        },
        {
          id: 'path_1',
          node: {
            id: 'e2',
            label: 'Airport 2',
            relation: undefined,
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should handle average logic', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology(
      {
        id: 'e1',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 1',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const l1 = graph.addLogicPill2Graphology({
      id: 'l1',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Logic LT',
      logic: MathFilters[NumberFilterTypes.LESS_THAN],
      attributes: [],
      inputs: {},
    });

    const l2 = graph.addLogicPill2Graphology({
      id: 'l2',
      type: QueryElementTypes.Logic,
      x: 100,
      y: 100,
      name: 'Logic average',
      logic: MathAggregations[NumberAggregationTypes.AVG],
      attributes: [],
      inputs: {},
    });

    graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });
    graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: NumberAggregationTypes.AVG, targetHandleName: 'Value' });

    const expected: BackendQueryFormat = {
      ...defaultQuery,
      logic: ['<', ['Avg', '@e1.age'], 0],
      machineLearning: [],
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: undefined,
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });

  it('should allow custom values in logic pills', () => {
    const graph = new QueryMultiGraphology();

    const e1 = graph.addPill2Graphology(
      {
        id: 'e1',
        type: QueryElementTypes.Entity,
        x: 100,
        y: 100,
        name: 'Airport 1',
        attributes: [],
      },
      [{ name: 'age', type: 'string' }],
    );

    const l1 = graph.addLogicPill2Graphology(
      {
        id: 'l1',
        type: QueryElementTypes.Logic,
        x: 100,
        y: 100,
        name: 'Logic LT',
        logic: MathFilters[NumberFilterTypes.LESS_THAN],
        attributes: [],
        inputs: {},
      },
      { '1': 5 },
    );

    graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' });

    const expected: BackendQueryFormat = {
      ...defaultQuery,
      logic: ['<', '@e1.age', 5],
      machineLearning: [],
      query: [
        {
          id: 'path_0',
          node: {
            id: 'e1',
            label: 'Airport 1',
            relation: undefined,
          },
        },
      ],
    };

    let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
    expect(ret).toMatchObject(expected);
  });
});

it('should no connections between entities and relations', () => {
  const graph = new QueryMultiGraphology();

  const e1 = graph.addPill2Graphology(
    {
      id: 'e1',
      type: QueryElementTypes.Entity,
      x: 100,
      y: 100,
      name: 'Airport 1',
      attributes: [],
    },
    [{ name: 'age', type: 'string' }],
  );

  const e2 = graph.addPill2Graphology(
    {
      id: 'e2',
      type: QueryElementTypes.Entity,
      x: 100,
      y: 100,
      name: 'Airport 2',
      attributes: [],
    },
    [{ name: 'age', type: 'string' }],
  );
  const r1 = graph.addPill2Graphology(
    {
      id: 'r1',
      type: QueryElementTypes.Relation,
      x: 100,
      y: 100,
      name: 'Relation 1',
      depth: { min: 0, max: 1 },
      attributes: [],
      collection: 'r',
    },
    [{ name: 'age', type: 'string' }],
  );

  const expected: BackendQueryFormat = {
    ...defaultQuery,
    logic: undefined,
    machineLearning: [],
    query: [
      {
        id: 'path_0',
        node: {
          id: 'e1',
          label: 'Airport 1',
          relation: undefined,
        },
      },
      {
        id: 'path_1',
        node: {
          id: 'e2',
          label: 'Airport 2',
          relation: undefined,
        },
      },
      {
        id: 'path_2',
        node: {
          relation: {
            id: 'r1',
            label: 'Relation 1',
            direction: 'TO',
            node: {},
            depth: { min: 0, max: 1 },
          },
        },
      },
    ],
  };

  let ret = Query2BackendQuery('database', graph.export(), defaultSettings);
  expect(ret).toMatchObject(expected);
});