import { expect } from 'chai'
import sinon from 'sinon'
import { mount } from '@vue/test-utils'
import mutations from '@/store/mutations'
import { createStore } from 'vuex'
import Tab from '@/components/Tab'
import { nextTick } from 'vue'

let place

describe('Tab.vue', () => {
  beforeEach(() => {
    place = document.createElement('div')
    document.body.appendChild(place)
  })

  afterEach(() => {
    sinon.restore()
    place.remove()
  })

  it('Renders passed query', async () => {
    // mock store state
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true
    }
    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true, 'icon-button': true },
        plugins: [store]
      },
      props: {
        tab: {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          viewType: 'chart',
          viewOptions: {},
          layout: {
            sqlEditor: 'above',
            table: 'bottom',
            dataView: 'hidden'
          },
          isPredefined: false,
          result: null,
          isGettingResults: false,
          error: null,
          time: 0
        }
      }
    })
    await nextTick()
    expect(wrapper.find('.tab-content-container').isVisible()).to.equal(true)
    expect(wrapper.find('.bottomPane .run-result-panel').exists()).to.equal(
      true
    )
    expect(
      wrapper.find('.run-result-panel .result-before').isVisible()
    ).to.equal(true)
    expect(
      wrapper.find('.above .sql-editor-panel .codemirror-container').text()
    ).to.contain('SELECT * FROM foo')
  })

  it("Doesn't render tab when it's not active", async () => {
    // mock store state
    const state = {
      currentTabId: 0,
      isWorkspaceVisible: true
    }
    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab: {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          viewType: 'chart',
          viewOptions: {},
          layout: {
            sqlEditor: 'above',
            table: 'bottom',
            dataView: 'hidden'
          },
          isPredefined: false,
          result: null,
          isGettingResults: false,
          error: null,
          time: 0
        }
      }
    })
    await nextTick()
    expect(wrapper.find('.tab-content-container').isVisible()).to.equal(false)
  })

  it('Is not visible when not active', async () => {
    // mock store state
    const state = {
      currentTabId: 0,
      isWorkspaceVisible: true
    }

    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab: {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          viewType: 'chart',
          viewOptions: {},
          layout: {
            sqlEditor: 'above',
            table: 'bottom',
            dataView: 'hidden'
          },
          isPredefined: false,
          result: null,
          isGettingResults: false,
          error: null,
          time: 0
        }
      }
    })
    await nextTick()
    expect(wrapper.find('.tab-content-container').isVisible()).to.equal(false)
  })

  it('Updates tab state when a query is changed', async () => {
    // mock store state
    const state = {
      isWorkspaceVisible: true,
      tabs: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          viewType: 'chart',
          viewOptions: {},
          layout: {
            sqlEditor: 'above',
            table: 'bottom',
            dataView: 'hidden'
          },
          isPredefined: false,
          result: null,
          isGettingResults: false,
          error: null,
          time: 0,
          isSaved: true
        }
      ],
      currentTabId: 1
    }

    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab: store.state.tabs[0]
      }
    })
    await nextTick()
    await wrapper.findComponent({ name: 'SqlEditor' }).setValue(' limit 100')
    expect(store.state.tabs[0].isSaved).to.equal(false)
  })

  it('Updates tab state when data view settings are changed', async () => {
    // mock store state
    const state = {
      isWorkspaceVisible: true,
      tabs: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          viewType: 'chart',
          viewOptions: {},
          layout: {
            sqlEditor: 'above',
            table: 'bottom',
            dataView: 'hidden'
          },
          isPredefined: false,
          result: null,
          isGettingResults: false,
          error: null,
          time: 0,
          isSaved: true
        }
      ],
      currentTabId: 1
    }

    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab: store.state.tabs[0]
      }
    })
    await nextTick()
    await wrapper.findComponent({ name: 'DataView' }).vm.$emit('update')
    expect(store.state.tabs[0].isSaved).to.equal(false)
  })

  it('Shows .result-in-progress message when executing query', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    // mock store state
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true,
      tabs: [tab]
    }

    const store = createStore({ state, mutations })
    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true, 'icon-button': true },
        plugins: [store]
      },
      props: {
        tab
      }
    })
    await nextTick()

    store.state.tabs[0].isGettingResults = true
    await nextTick()
    expect(
      wrapper.find('.run-result-panel .result-in-progress').exists()
    ).to.equal(true)
  })

  it('Shows error when executing query ends with error', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    // mock store state
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true,
      tabs: [tab]
    }

    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true, 'icon-button': true },
        plugins: [store]
      },
      props: {
        tab: store.state.tabs[0]
      }
    })
    await nextTick()
    store.state.tabs[0].error = {
      type: 'error',
      message: 'There is no table foo'
    }
    await nextTick()
    expect(
      wrapper.find('.run-result-panel .result-before').isVisible()
    ).to.equal(false)
    expect(
      wrapper.find('.run-result-panel .result-in-progress').exists()
    ).to.equal(false)
    expect(wrapper.findComponent({ name: 'logs' }).isVisible()).to.equal(true)
    expect(wrapper.findComponent({ name: 'logs' }).text()).to.include(
      'There is no table foo'
    )
  })

  it('Passes result to sql-table component', async () => {
    const result = {
      columns: ['id', 'name'],
      values: {
        id: [1, 2],
        name: ['foo', 'bar']
      }
    }
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }
    // mock store state
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true,
      tabs: [tab]
    }

    const store = createStore({ state, mutations })

    // mount the component
    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab
      }
    })
    await nextTick()
    store.state.tabs[0].result = result
    await nextTick()
    expect(
      wrapper.find('.run-result-panel .result-before').isVisible()
    ).to.equal(false)
    expect(
      wrapper.find('.run-result-panel .result-in-progress').exists()
    ).to.equal(false)
    expect(wrapper.findComponent({ name: 'logs' }).exists()).to.equal(false)
    expect(wrapper.findComponent({ name: 'SqlTable' }).vm.dataSet).to.eql(
      result
    )
  })

  it('Switches views', async () => {
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true
    }

    const store = createStore({ state, mutations })

    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab
      }
    })

    let tableBtn = wrapper
      .find('.above .side-tool-bar')
      .findComponent({ ref: 'tableBtn' })
    await tableBtn.vm.$emit('click')

    expect(wrapper.find('.bottomPane .sql-editor-panel').exists()).to.equal(
      true
    )
    expect(wrapper.find('.above .run-result-panel').exists()).to.equal(true)

    const dataViewBtn = wrapper
      .find('.above .side-tool-bar')
      .findComponent({ ref: 'dataViewBtn' })
    await dataViewBtn.vm.$emit('click')

    expect(wrapper.find('.bottomPane .sql-editor-panel').exists()).to.equal(
      true
    )
    expect(wrapper.find('.above .data-view-panel').exists()).to.equal(true)

    const sqlEditorBtn = wrapper
      .find('.above .side-tool-bar')
      .findComponent({ ref: 'sqlEditorBtn' })
    await sqlEditorBtn.vm.$emit('click')

    expect(wrapper.find('.above .sql-editor-panel').exists()).to.equal(true)
    expect(wrapper.find('.bottomPane .data-view-panel').exists()).to.equal(true)

    tableBtn = wrapper
      .find('.bottomPane .side-tool-bar')
      .findComponent({ ref: 'tableBtn' })
    await tableBtn.vm.$emit('click')

    expect(wrapper.find('.above .sql-editor-panel').exists()).to.equal(true)
    expect(wrapper.find('.bottomPane .run-result-panel').exists()).to.equal(
      true
    )
  })

  it('Maximize top panel if maximized panel is above', async () => {
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true
    }
    const store = createStore({ state, mutations })
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      maximize: 'sqlEditor',
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab
      }
    })
    await nextTick()

    expect(wrapper.find('.above').element.parentElement.style.height).to.equal(
      '100%'
    )
  })

  it('Maximize bottom panel if maximized panel is below', async () => {
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true
    }
    const store = createStore({ state, mutations })
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      maximize: 'table',
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab
      }
    })
    await nextTick()
    expect(
      wrapper.find('.bottomPane').element.parentElement.style.height
    ).to.equal('100%')
  })

  it('Panel size is 50 if nothing to maximize', async () => {
    const state = {
      currentTabId: 1,
      isWorkspaceVisible: true
    }
    const store = createStore({ state, mutations })
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo; CREATE TABLE bar(a,b);',
      viewType: 'chart',
      viewOptions: {},
      layout: {
        sqlEditor: 'above',
        table: 'bottom',
        dataView: 'hidden'
      },
      isPredefined: false,
      result: null,
      isGettingResults: false,
      error: null,
      time: 0,
      isSaved: true
    }

    const wrapper = mount(Tab, {
      attachTo: place,
      global: {
        stubs: { chart: true },
        plugins: [store]
      },
      props: {
        tab
      }
    })

    await nextTick()
    expect(wrapper.find('.above').element.parentElement.style.height).to.equal(
      '50%'
    )
    expect(
      wrapper.find('.bottomPane').element.parentElement.style.height
    ).to.equal('50%')
  })
})
