import {
  TreeGroupNode,
  createFiltersTree,
  matchSearchFilters,
} from '@baserow/modules/database/utils/view'
import { TestApp } from '@baserow/test/helpers/testApp'
import _ from 'lodash'

describe('TreeGroupNode', () => {
  it('should initialize correctly', () => {
    const node = new TreeGroupNode('AND')
    expect(node.filterType).toBe('AND')
    expect(node.parent).toBeNull()
    expect(node.filters).toEqual([])
    expect(node.children).toEqual([])
  })

  it('should add child nodes', () => {
    const parentNode = new TreeGroupNode('AND')
    const childNode = new TreeGroupNode('OR', parentNode)
    expect(parentNode.children[0]).toBe(childNode)
  })
})

describe('createFiltersTree', () => {
  it('should create a tree with root node', () => {
    const rootNode = createFiltersTree('AND', [], [])
    expect(rootNode.filterType).toBe('AND')
    expect(rootNode.hasFilters()).toBe(false)
    expect(rootNode.filters).toEqual([])
    expect(rootNode.children).toEqual([])
  })

  it('should correctly add filters to the tree', () => {
    const filters = [
      { field: 1, type: 'type1', value: 'value1', group: 1 },
      { field: 2, type: 'type2', value: 'value2', group: 2 },
      { field: 3, type: 'type3', value: 'value3' },
    ]
    const filterGroups = [
      { id: 1, filter_type: 'AND' },
      { id: 2, filter_type: 'OR' },
      { id: 3, filter_type: 'AND' },
    ]
    const rootNode = createFiltersTree('AND', filters, filterGroups)
    expect(rootNode.filterType).toBe('AND')
    expect(rootNode.hasFilters()).toBe(true)
    expect(rootNode.filters).toEqual([
      { field: 3, type: 'type3', value: 'value3' },
    ])
    expect(rootNode.children).toHaveLength(3)
    expect(rootNode.children[0].filterType).toBe('AND')
    expect(rootNode.children[0].filters).toEqual([
      { field: 1, type: 'type1', value: 'value1', group: 1 },
    ])
    expect(rootNode.children[0].children).toEqual([])
    expect(rootNode.children[1].filterType).toBe('OR')
    expect(rootNode.children[1].filters).toEqual([
      { field: 2, type: 'type2', value: 'value2', group: 2 },
    ])
    expect(rootNode.children[2].filters).toEqual([])
    expect(rootNode.children[2].hasFilters()).toBe(false)
  })

  it('should correctly nest groups into the tree', () => {
    const filterGroups = [
      { id: 1, filter_type: 'AND' },
      { id: 2, filter_type: 'OR', parent_group: 1 },
      { id: 3, filter_type: 'AND', parent_group: 1 },
      { id: 4, filter_type: 'OR', parent_group: 3 },
    ]
    const rootNode = createFiltersTree('AND', [], filterGroups)
    expect(rootNode.hasFilters()).toBe(false)
    expect(rootNode.children).toHaveLength(1)
    expect(rootNode.children[0].filterType).toBe('AND')
    expect(rootNode.children[0].children).toHaveLength(2)
    expect(rootNode.children[0].children[0].filterType).toBe('OR')
    expect(rootNode.children[0].children[0].children).toEqual([])
    expect(rootNode.children[0].children[1].filterType).toBe('AND')
    expect(rootNode.children[0].children[1].children).toHaveLength(1)
    expect(rootNode.children[0].children[1].children[0].filterType).toBe('OR')
    expect(rootNode.children[0].children[1].children[0].children).toEqual([])
  })

  it('should correctly add filters to nested groups', () => {
    const filterGroups = [
      { id: 1, filter_type: 'AND' },
      { id: 2, filter_type: 'OR', parent_group: 1 },
      { id: 3, filter_type: 'OR', parent_group: 2 },
    ]
    const rootNode = createFiltersTree('AND', [], filterGroups)
    expect(rootNode.hasFilters()).toBe(false)
    rootNode.children[0].children[0].children[0].addFilter({
      field: 1,
      type: 'type1',
      value: 'value1',
      group: 4,
    })
    expect(rootNode.hasFilters()).toBe(true)
  })
})

describe('matchSearchFilters', () => {
  let testApp = null
  let registry = null

  beforeAll(() => {
    testApp = new TestApp()
    registry = testApp.getRegistry()
  })

  afterEach((done) => {
    testApp.afterEach().then(done)
  })

  it('should return true with no filters', () => {
    const filters = []
    const filterGroups = []
    const fields = {}
    const rowValues = {}
    expect(
      matchSearchFilters(
        registry,
        'AND',
        filters,
        filterGroups,
        fields,
        rowValues
      )
    ).toBe(true)
  })

  it('should match a single filter', () => {
    const filters = [{ field: 1, type: 'equal', value: 'a' }]
    const filterGroups = []
    const fields = [{ id: 1, type: 'text' }]
    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'a',
      })
    ).toBe(true)

    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'b',
      })
    ).toBe(false)
  })

  it('should match a single filter in a group', () => {
    const filters = [{ field: 1, type: 'equal', value: 'a', group: 1 }]
    const filterGroups = [{ filter_type: 'OR', id: 1 }]
    const fields = [{ id: 1, type: 'text' }]
    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'a',
      })
    ).toBe(true)
    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'b',
      })
    ).toBe(false)
  })

  it('should match filters correctly with OR and AND', () => {
    // Match rows where field_1=Ada OR (field_1=John AND field_2=Turing)
    const filters = [
      { field: 1, type: 'equal', value: 'Ada' },
      { field: 1, type: 'equal', value: 'John', group: 1 },
      { field: 2, type: 'equal', value: 'Turing', group: 1 },
    ]
    const filterGroups = [{ filter_type: 'AND', id: 1 }]
    const fields = [
      { id: 1, type: 'text' },
      { id: 2, type: 'text' },
    ]

    expect(
      matchSearchFilters(registry, 'OR', filters, filterGroups, fields, {
        field_1: 'Ada',
        field_2: 'Lovelace',
      })
    ).toBe(true)

    expect(
      matchSearchFilters(registry, 'OR', filters, filterGroups, fields, {
        field_1: 'Alan',
        field_2: 'Turing',
      })
    ).toBe(false)

    expect(
      matchSearchFilters(registry, 'OR', filters, filterGroups, fields, {
        field_1: 'John',
        field_2: 'Travolta',
      })
    ).toBe(false)

    expect(
      matchSearchFilters(registry, 'OR', filters, filterGroups, fields, {
        field_1: 'John',
        field_2: 'Turing',
      })
    ).toBe(true)
  })

  it('should match filters correctly with AND and OR', () => {
    // Match rows where field_1=John AND (field_2=Travolta OR field_2=Turing)
    const filters = [
      { field: 1, type: 'equal', value: 'John' },
      { field: 2, type: 'equal', value: 'Travolta', group: 1 },
      { field: 2, type: 'equal', value: 'Turing', group: 1 },
    ]
    const filterGroups = [{ filter_type: 'OR', id: 1 }]
    const fields = [
      { id: 1, type: 'text' },
      { id: 2, type: 'text' },
    ]

    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'John',
        field_2: 'Lennon',
      })
    ).toBe(false)

    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'Alan',
        field_2: 'Turing',
      })
    ).toBe(false)

    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'John',
        field_2: 'Travolta',
      })
    ).toBe(true)

    expect(
      matchSearchFilters(registry, 'AND', filters, filterGroups, fields, {
        field_1: 'John',
        field_2: 'Turing',
      })
    ).toBe(true)
  })

  it('should serialize the filter tree correctly', () => {
    const filters = [
      { field: 1, type: 'equal', value: 'a' },
      { field: 2, type: 'equal', value: 'b', group: 1 },
      { field: 3, type: 'equal', value: 'c', group: 1 },
    ]
    const filterGroups = [{ filter_type: 'OR', id: 1 }]
    const rootNode = createFiltersTree('AND', filters, filterGroups)
    expect(
      _.isEqual(rootNode.getFiltersTreeSerialized(), {
        filter_type: 'AND',
        filters: [{ type: 'equal', field: 1, value: 'a' }],
        groups: [
          {
            filter_type: 'OR',
            filters: [
              { type: 'equal', field: 2, value: 'b' },
              { type: 'equal', field: 3, value: 'c' },
            ],
            groups: [],
          },
        ],
      })
    ).toBe(true)
  })
})