import { expect } from 'chai'
import sinon from 'sinon'
import { mount, shallowMount } from '@vue/test-utils'
import { createStore } from 'vuex'
import MainMenu from '@/components/MainMenu'
import storedInquiries from '@/lib/storedInquiries'
import { nextTick } from 'vue'
import eventBus from '@/lib/eventBus'
import actions from '@/store/actions'
import mutations from '@/store/mutations'

let wrapper = null

describe('MainMenu.vue', () => {
  let clock

  beforeEach(() => {
    clock = sinon.useFakeTimers()
    sinon.spy(eventBus, '$emit')
    sinon.spy(eventBus, '$off')
  })

  afterEach(() => {
    sinon.restore()

    // We need explicitly destroy the component, so that beforeUnmount hook was called
    // It's important because in this hook MainMenu component removes keydown event listener.
    wrapper.unmount()
  })

  it('Create, Save and Save as are visible only on /workspace page', async () => {
    const state = {
      currentTab: { query: '', execute: sinon.stub() },
      tabs: [{}],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }
    // mount the component on workspace
    wrapper = shallowMount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    expect(wrapper.find('#save-btn').exists()).to.equal(true)
    expect(wrapper.find('#save-btn').isVisible()).to.equal(true)
    expect(wrapper.find('#save-as-btn').exists()).to.equal(true)
    expect(wrapper.find('#save-as-btn').isVisible()).to.equal(true)
    expect(wrapper.find('#create-btn').exists()).to.equal(true)
    expect(wrapper.find('#create-btn').isVisible()).to.equal(true)
    wrapper.unmount()

    $route.path = '/inquiries'
    // mount the component on inquiries
    wrapper = shallowMount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    expect(wrapper.find('#save-btn').exists()).to.equal(true)
    expect(wrapper.find('#save-btn').isVisible()).to.equal(false)
    expect(wrapper.find('#create-btn').exists()).to.equal(true)
    expect(wrapper.find('#create-btn').isVisible()).to.equal(true)
  })

  it('Save and Save as are not visible if there is no tabs', () => {
    const state = {
      currentTab: null,
      tabs: [],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }
    wrapper = shallowMount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    expect(wrapper.find('#save-btn').exists()).to.equal(true)
    expect(wrapper.find('#save-btn').isVisible()).to.equal(false)
    expect(wrapper.find('#save-as-btn').exists()).to.equal(true)
    expect(wrapper.find('#save-as-btn').isVisible()).to.equal(false)
    expect(wrapper.find('#create-btn').exists()).to.equal(true)
    expect(wrapper.find('#create-btn').isVisible()).to.equal(true)
  })

  it('Save is disabled if current tab.isSaved is true', async () => {
    const tab = {
      id: 1,
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    const vm = wrapper.vm
    expect(wrapper.find('#save-btn').element.disabled).to.equal(false)
    expect(wrapper.find('#save-as-btn').element.disabled).to.equal(false)

    store.state.tabs[0].isSaved = true
    await vm.$nextTick()
    expect(wrapper.find('#save-btn').element.disabled).to.equal(true)
    expect(wrapper.find('#save-as-btn').element.disabled).to.equal(false)
  })

  it('Creates a tab', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const newInquiryId = 1
    const actions = {
      addTab: sinon.stub().resolves(newInquiryId)
    }
    const mutations = {
      setCurrentTabId: sinon.stub()
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    const $router = { push: sinon.stub() }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route, $router },
        stubs: ['router-link'],
        plugins: [store]
      }
    })

    await wrapper.find('#create-btn').trigger('click')
    expect(actions.addTab.calledOnce).to.equal(true)
    await actions.addTab.returnValues[0]
    expect(
      mutations.setCurrentTabId.calledOnceWith(state, newInquiryId)
    ).to.equal(true)
    expect($router.push.calledOnce).to.equal(false)
  })

  it('Creates a tab and redirects to workspace', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const newInquiryId = 1
    const actions = {
      addTab: sinon.stub().resolves(newInquiryId)
    }
    const mutations = {
      setCurrentTabId: sinon.stub()
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/inquiries' }
    const $router = { push: sinon.stub() }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route, $router },
        stubs: ['router-link'],
        plugins: [store]
      }
    })

    await wrapper.find('#create-btn').trigger('click')
    expect(actions.addTab.calledOnce).to.equal(true)
    await actions.addTab.returnValues[0]
    expect(
      mutations.setCurrentTabId.calledOnceWith(state, newInquiryId)
    ).to.equal(true)
    expect($router.push.calledOnce).to.equal(true)
  })

  it('Ctrl R calls currentTab.execute if running is enabled and route.path is "/workspace"', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }
    const $router = { push: sinon.stub() }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route, $router },
        stubs: ['router-link'],
        plugins: [store]
      }
    })

    const ctrlR = new KeyboardEvent('keydown', { key: 'r', ctrlKey: true })
    const metaR = new KeyboardEvent('keydown', { key: 'r', metaKey: true })
    // Running is enabled and route path is workspace
    document.dispatchEvent(ctrlR)
    expect(state.currentTab.execute.calledOnce).to.equal(true)
    document.dispatchEvent(metaR)
    expect(state.currentTab.execute.calledTwice).to.equal(true)

    // Running is disabled and route path is workspace
    store.state.db = null
    document.dispatchEvent(ctrlR)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
    document.dispatchEvent(metaR)
    expect(state.currentTab.execute.calledTwice).to.equal(true)

    // Running is enabled and route path is not workspace
    state.db = {}
    wrapper.vm.$route.path = '/inquiries'
    document.dispatchEvent(ctrlR)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
    document.dispatchEvent(metaR)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
  })

  it('Ctrl Enter calls currentTab.execute if running is enabled and route.path is "/workspace"', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }
    const $router = { push: sinon.stub() }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route, $router },
        stubs: ['router-link'],
        plugins: [store]
      }
    })

    const ctrlEnter = new KeyboardEvent('keydown', {
      key: 'Enter',
      ctrlKey: true
    })
    const metaEnter = new KeyboardEvent('keydown', {
      key: 'Enter',
      metaKey: true
    })
    // Running is enabled and route path is workspace
    document.dispatchEvent(ctrlEnter)
    expect(state.currentTab.execute.calledOnce).to.equal(true)
    document.dispatchEvent(metaEnter)
    expect(state.currentTab.execute.calledTwice).to.equal(true)

    // Running is disabled and route path is workspace
    store.state.db = null
    document.dispatchEvent(ctrlEnter)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
    document.dispatchEvent(metaEnter)
    expect(state.currentTab.execute.calledTwice).to.equal(true)

    // Running is enabled and route path is not workspace
    store.state.db = {}
    wrapper.vm.$route.path = '/inquiries'
    document.dispatchEvent(ctrlEnter)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
    document.dispatchEvent(metaEnter)
    expect(state.currentTab.execute.calledTwice).to.equal(true)
  })

  it('Ctrl B calls createNewInquiry', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    sinon.stub(wrapper.vm, 'createNewInquiry')

    const ctrlB = new KeyboardEvent('keydown', { key: 'b', ctrlKey: true })
    const metaB = new KeyboardEvent('keydown', { key: 'b', metaKey: true })
    document.dispatchEvent(ctrlB)
    expect(wrapper.vm.createNewInquiry.calledOnce).to.equal(true)
    document.dispatchEvent(metaB)
    expect(wrapper.vm.createNewInquiry.calledTwice).to.equal(true)

    wrapper.vm.$route.path = '/inquiries'
    document.dispatchEvent(ctrlB)
    expect(wrapper.vm.createNewInquiry.calledThrice).to.equal(true)
    document.dispatchEvent(metaB)
    expect(wrapper.vm.createNewInquiry.callCount).to.equal(4)
  })

  it('Ctrl S calls onSave if the tab is unsaved and route path is /workspace', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    sinon.stub(wrapper.vm, 'onSave')

    const ctrlS = new KeyboardEvent('keydown', { key: 's', ctrlKey: true })
    const metaS = new KeyboardEvent('keydown', { key: 's', metaKey: true })
    // tab is unsaved and route is /workspace
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSave.calledOnce).to.equal(true)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSave.calledTwice).to.equal(true)

    // tab is saved and route is /workspace
    store.state.tabs[0].isSaved = true
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSave.calledTwice).to.equal(true)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSave.calledTwice).to.equal(true)

    // tab is unsaved and route is not /workspace
    wrapper.vm.$route.path = '/inquiries'
    store.state.tabs[0].isSaved = false
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSave.calledTwice).to.equal(true)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSave.calledTwice).to.equal(true)
  })

  it('Ctrl Shift S calls onSaveAs if route path is /workspace', async () => {
    const tab = {
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state })
    const $route = { path: '/workspace' }

    wrapper = shallowMount(MainMenu, {
      global: {
        mocks: { $route },
        stubs: ['router-link'],
        plugins: [store]
      }
    })
    sinon.stub(wrapper.vm, 'onSaveAs')

    const ctrlS = new KeyboardEvent('keydown', {
      key: 'S',
      ctrlKey: true,
      shiftKey: true
    })
    const metaS = new KeyboardEvent('keydown', {
      key: 'S',
      metaKey: true,
      shiftKey: true
    })
    // tab is unsaved and route is /workspace
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSaveAs.calledOnce).to.equal(true)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSaveAs.calledTwice).to.equal(true)

    // tab is saved and route is /workspace
    store.state.tabs[0].isSaved = true
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSaveAs.calledThrice).to.equal(true)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSaveAs.callCount).to.equal(4)

    // tab is unsaved and route is not /workspace
    wrapper.vm.$route.path = '/inquiries'
    store.state.tabs[0].isSaved = false
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSaveAs.callCount).to.equal(4)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSaveAs.callCount).to.equal(4)

    // tab is saved and route is not /workspace
    wrapper.vm.$route.path = '/inquiries'
    store.state.tabs[0].isSaved = true
    document.dispatchEvent(ctrlS)
    expect(wrapper.vm.onSaveAs.callCount).to.equal(4)
    document.dispatchEvent(metaS)
    expect(wrapper.vm.onSaveAs.callCount).to.equal(4)
  })

  it('Saves the inquiry when no need the new name and no update conflict', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      updatedAt: '2025-05-15T15:30:00Z',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      inquiries: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM foo',
          updatedAt: '2025-05-15T15:30:00Z',
          createdAt: '2025-05-14T15:30:00Z'
        }
      ],
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo',
        id: 1,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-16T15:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(false)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the dialog is closed
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: ''
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'foo',
            id: 1,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-16T15:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Inquiry conflict: overwrite', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      updatedAt: '2025-05-15T15:30:00Z',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      inquiries: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM bar',
          updatedAt: '2025-05-15T16:30:00Z',
          createdAt: '2025-05-14T15:30:00Z'
        }
      ],
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo',
        id: 1,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-16T17:30:00Z',
        createdAt: '2025-05-14T15:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(false)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the conflict dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Inquiry saving conflict'
    )

    // find Overwrite in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Overwrite')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: ''
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'foo',
            id: 1,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-16T17:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Inquiry conflict after saving new inquiry: overwrite', async () => {
    const tab = {
      id: 1,
      name: null,
      query: 'SELECT * FROM foo',
      updatedAt: undefined,
      execute: sinon.stub(),
      dataView: { getOptionsForSave: sinon.stub() },
      isSaved: false
    }
    const state = {
      currentTab: tab,
      inquiries: [],
      tabs: [tab],
      db: {}
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that Save dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // enter the name
    await wrapper.find('.dialog-body input').setValue('foo')

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that now there is one inquiry saved
    expect(state.inquiries.length).to.equal(1)
    expect(state.inquiries[0].name).to.equal('foo')
    expect(state.tabs[0].name).to.equal('foo')

    // change the inquiry in store (like it's updated in another tab)
    store.state.inquiries[0].query = 'SELECT * FROM foo_updated_in_another_tab'
    store.state.inquiries[0].updatedAt = '2025-05-15T00:00:10Z'
    store.state.currentTab.query = 'SELECT * FROM foo_new'
    store.state.currentTab.isSaved = false
    await nextTick()
    await wrapper.find('#save-btn').trigger('click')

    // check that the conflict dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Inquiry saving conflict'
    )

    // find Overwrite in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Overwrite')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that it's still one inquiry saved
    expect(state.inquiries.length).to.equal(1)
    expect(state.inquiries[0].name).to.equal('foo')
    expect(state.tabs[0].name).to.equal('foo')
    expect(state.inquiries[0].query).to.equal('SELECT * FROM foo_new')
    expect(state.tabs[0].query).to.equal('SELECT * FROM foo_new')
  })

  it('Inquiry conflict: save as new', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      updatedAt: '2025-05-15T15:30:00Z',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      inquiries: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM bar',
          updatedAt: '2025-05-15T16:30:00Z',
          createdAt: '2025-05-14T15:30:00Z'
        }
      ],
      tabs: [tab]
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo_new',
        id: 2,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-16T17:30:00Z',
        createdAt: '2025-05-16T17:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(false)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')
    // check that the conflict dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Inquiry saving conflict'
    )

    await clock.tick(100)

    // find "Save as new" in the dialog and click

    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save as new')
      .trigger('click')

    // Hiding any dialog is done with tiny animation. Give time to finish it:
    await clock.tick(100)
    // Note: don't call nextTick before clock.tick. That leads to extra trap in
    // trapStack and the test fails with focus-trap error in afterEach hook
    // when unmount the component

    // check that only one dialog open
    expect(wrapper.findAll('.dialog.vfm').length).to.equal(1)
    // enter the new name
    await wrapper.find('.dialog-body input').setValue('foo_new')

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='foo_new')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: 'foo_new'
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'foo_new',
            id: 2,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-16T17:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Inquiry conflict: cancel', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      updatedAt: '2025-05-15T15:30:00Z',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      inquiries: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM bar',
          updatedAt: '2025-05-15T16:30:00Z',
          createdAt: '2025-05-14T15:30:00Z'
        }
      ],
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub()
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(false)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the conflict dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Inquiry saving conflict'
    )

    // find Cancel in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Cancel')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was not saved via storedInquiries.save
    expect(actions.saveInquiry.called).to.equal(false)

    // check that the tab was not updated
    expect(mutations.updateTab.called).to.equal(false)

    // check that 'inquirySaved' event is not listened on eventBus
    expect(eventBus.$off.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Shows en error when the new name is needed but not specifyied', async () => {
    const tab = {
      id: 1,
      name: null,
      tempName: 'Untitled',
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false,
      updatedAt: '2025-05-15T15:30:00Z'
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo',
        id: 1,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-16T15:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(true)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that we have an error message and dialog is still open
    expect(wrapper.find('.text-field-error').text()).to.equal(
      "Inquiry name can't be empty"
    )
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
  })

  it('Saves the new inquiry with a new name', async () => {
    const tab = {
      id: 1,
      name: null,
      tempName: 'Untitled',
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false,
      updatedAt: undefined
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo',
        id: 2,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-15T15:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(true)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // enter the new name
    await wrapper.find('.dialog-body input').setValue('foo')

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='foo')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: 'foo'
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'foo',
            id: 2,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-15T15:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Saves a predefined inquiry with a new name', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isPredefined: true,
      result: {
        columns: ['id', 'name'],
        values: [
          [1, 'Harry Potter'],
          [2, 'Drako Malfoy']
        ]
      },
      viewType: 'chart',
      viewOptions: [],
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'bar',
        id: 2,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-15T15:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(true)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // check that save-note is visible (save-note is an explanation why do we need a new name)
    expect(wrapper.find('#save-note').isVisible()).to.equal(true)

    // enter the new name
    await wrapper.find('.dialog-body input').setValue('bar')

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='bar')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: 'bar'
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'bar',
            id: 2,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-15T15:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Cancel saving', async () => {
    const tab = {
      id: 1,
      name: null,
      tempName: 'Untitled',
      query: 'SELECT * FROM foo',
      execute: sinon.stub(),
      isSaved: false
    }
    const state = {
      currentTab: tab,
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'bar',
        id: 2,
        query: 'SELECT * FROM foo',
        viewType: 'chart'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(true)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-btn').trigger('click')

    // check that the dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // find Cancel in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Cancel')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was not saved via storedInquiries.save
    expect(actions.saveInquiry.called).to.equal(false)

    // check that the tab was not updated
    expect(mutations.updateTab.called).to.equal(false)

    // check that 'inquirySaved' event is not listened on eventBus
    expect(eventBus.$off.calledOnceWith('inquirySaved')).to.equal(true)
  })

  it('Save the inquiry as new', async () => {
    const tab = {
      id: 1,
      name: 'foo',
      query: 'SELECT * FROM foo',
      updatedAt: '2025-05-15T15:30:00Z',
      execute: sinon.stub(),
      isSaved: true
    }
    const state = {
      currentTab: tab,
      inquiries: [
        {
          id: 1,
          name: 'foo',
          query: 'SELECT * FROM bar',
          updatedAt: '2025-05-15T16:30:00Z',
          createdAt: '2025-05-14T15:30:00Z'
        }
      ],
      tabs: [tab],
      db: {}
    }
    const mutations = {
      updateTab: sinon.stub()
    }
    const actions = {
      saveInquiry: sinon.stub().returns({
        name: 'foo_new',
        id: 2,
        query: 'SELECT * FROM foo',
        viewType: 'chart',
        viewOptions: [],
        updatedAt: '2025-05-16T17:30:00Z',
        createdAt: '2025-05-16T17:30:00Z'
      })
    }
    const store = createStore({ state, mutations, actions })
    const $route = { path: '/workspace' }
    sinon.stub(storedInquiries, 'isTabNeedName').returns(false)

    wrapper = mount(MainMenu, {
      attachTo: document.body,
      global: {
        mocks: { $route },
        stubs: {
          'router-link': true,
          'app-diagnostic-info': true,
          teleport: true,
          transition: false
        },
        plugins: [store]
      }
    })

    await wrapper.find('#save-as-btn').trigger('click')

    // check that Save dialog is open
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(true)
    expect(wrapper.find('.dialog.vfm .dialog-header').text()).to.contain(
      'Save inquiry'
    )

    // enter the new name
    await wrapper.find('.dialog-body input').setValue('foo_new')

    // find Save in the dialog and click
    await wrapper
      .findAll('.dialog-buttons-container button')
      .find(button => button.text() === 'Save')
      .trigger('click')

    // check that the dialog is closed
    await clock.tick(100)
    expect(wrapper.find('.dialog.vfm').exists()).to.equal(false)

    // check that the inquiry was saved via saveInquiry (newName='foo_new')
    expect(actions.saveInquiry.calledOnce).to.equal(true)
    expect(actions.saveInquiry.args[0][1]).to.eql({
      inquiryTab: state.currentTab,
      newName: 'foo_new'
    })

    // check that the tab was updated
    expect(
      mutations.updateTab.calledOnceWith(
        state,
        sinon.match({
          tab,
          newValues: {
            name: 'foo_new',
            id: 2,
            query: 'SELECT * FROM foo',
            viewType: 'chart',
            viewOptions: [],
            isSaved: true,
            updatedAt: '2025-05-16T17:30:00Z'
          }
        })
      )
    ).to.equal(true)

    // check that 'inquirySaved' event was triggered on eventBus
    expect(eventBus.$emit.calledOnceWith('inquirySaved')).to.equal(true)
  })
})
