import { expect } from 'chai'
import { mount, flushPromises } from '@vue/test-utils'
import Pivot from '@/components/Pivot/index.vue'
import chartHelper from '@/lib/chartHelper'
import fIo from '@/lib/utils/fileIo'
import $ from 'jquery'
import sinon from 'sinon'
import pivotHelper from '@/components/Pivot/pivotHelper'

describe('Pivot.vue', () => {
  let container
  const $store = { state: { isWorkspaceVisible: true } }

  beforeEach(() => {
    container = document.createElement('div')
    document.body.appendChild(container)
  })

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

  it('renders pivot table', () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          rendererName: 'Table'
        }
      },
      global: {
        stubs: { chart: true }
      }
    })
    const colLabels = wrapper.findAll('.pivot-output thead th.pvtColLabel')
    expect(colLabels[0].text()).to.equal('2020')
    expect(colLabels[1].text()).to.equal('2021')
    const rows = wrapper.findAll('.pivot-output tbody tr')
    // row0: bar - 2 - 1
    expect(rows[0].find('th').text()).to.equal('bar')
    expect(rows[0].find('td.col0').text()).to.equal('2')
    expect(rows[0].find('td.col1').text()).to.equal('1')
    expect(rows[0].find('td.rowTotal').text()).to.equal('3')

    // row1: foo -  - 2
    expect(rows[1].find('th').text()).to.equal('foo')
    expect(rows[1].find('td.col0').text()).to.equal('')
    expect(rows[1].find('td.col1').text()).to.equal('1')
    expect(rows[1].find('td.rowTotal').text()).to.equal('1')
  })

  it('updates when dataSource changes', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          rendererName: 'Table'
        }
      },
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.setProps({
      dataSources: {
        item: ['foo', 'bar', 'bar', 'bar', 'foo', 'baz'],
        year: [2021, 2021, 2020, 2020, 2021, 2020]
      }
    })

    const colLabels = wrapper.findAll('.pivot-output thead th.pvtColLabel')
    expect(colLabels[0].text()).to.equal('2020')
    expect(colLabels[1].text()).to.equal('2021')
    const rows = wrapper.findAll('.pivot-output tbody tr')
    // row0: bar - 2 - 1
    expect(rows[0].find('th').text()).to.equal('bar')
    expect(rows[0].find('td.col0').text()).to.equal('2')
    expect(rows[0].find('td.col1').text()).to.equal('1')
    expect(rows[0].find('td.rowTotal').text()).to.equal('3')

    // row1: baz - 1 -
    expect(rows[1].find('th').text()).to.equal('baz')
    expect(rows[1].find('td.col0').text()).to.equal('1')
    expect(rows[1].find('td.col1').text()).to.equal('')
    expect(rows[1].find('td.rowTotal').text()).to.equal('1')

    // row2: foo -  - 2
    expect(rows[2].find('th').text()).to.equal('foo')
    expect(rows[2].find('td.col0').text()).to.equal('')
    expect(rows[2].find('td.col1').text()).to.equal('2')
    expect(rows[2].find('td.rowTotal').text()).to.equal('2')
  })

  it('returns options for save', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          rendererName: 'Table'
        }
      },
      global: {
        mocks: { $store }
      }
    })

    await wrapper.findComponent({ name: 'pivotUi' }).setValue({
      rows: ['year'],
      cols: ['item'],
      colOrder: 'value_a_to_z',
      rowOrder: 'value_z_to_a',
      aggregator: $.pivotUtilities.aggregators.Count(),
      aggregatorName: 'Count',
      renderer: $.pivotUtilities.renderers.Table,
      rendererName: 'Table',
      vals: []
    })
    sinon
      .stub(
        wrapper.findComponent({ ref: 'customChart' }).vm,
        'getOptionsForSave'
      )
      .returns({ here_are: 'custom chart settings' })

    let optionsForSave = wrapper.vm.getOptionsForSave()

    expect(optionsForSave.rows).to.eql(['year'])
    expect(optionsForSave.cols).to.eql(['item'])
    expect(optionsForSave.colOrder).to.equal('value_a_to_z')
    expect(optionsForSave.rowOrder).to.equal('value_z_to_a')
    expect(optionsForSave.aggregatorName).to.equal('Count')
    expect(optionsForSave.rendererName).to.equal('Table')
    expect(optionsForSave.rendererOptions).to.equal(undefined)
    expect(optionsForSave.vals).to.eql([])

    await wrapper.findComponent({ name: 'pivotUi' }).setValue({
      rows: ['item'],
      cols: ['year'],
      colOrder: 'value_a_to_z',
      rowOrder: 'value_z_to_a',
      aggregator: $.pivotUtilities.aggregators.Count(),
      aggregatorName: 'Count',
      renderer: $.pivotUtilities.renderers['Custom chart'],
      rendererName: 'Custom chart',
      vals: []
    })

    optionsForSave = wrapper.vm.getOptionsForSave()
    expect(optionsForSave.rows).to.eql(['item'])
    expect(optionsForSave.cols).to.eql(['year'])
    expect(optionsForSave.colOrder).to.equal('value_a_to_z')
    expect(optionsForSave.rowOrder).to.equal('value_z_to_a')
    expect(optionsForSave.aggregatorName).to.equal('Count')
    expect(optionsForSave.rendererName).to.equal('Custom chart')
    expect(optionsForSave.rendererOptions).to.eql({
      customChartOptions: { here_are: 'custom chart settings' }
    })
    expect(optionsForSave.vals).to.eql([])
  })

  it('prepareCopy returns canvas for tables and url for plotly charts', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          rendererName: 'Table'
        }
      },
      attachTo: container,
      global: {
        mocks: { $store }
      }
    })

    expect(await wrapper.vm.prepareCopy()).to.be.instanceof(HTMLCanvasElement)

    sinon
      .stub(wrapper.findComponent({ ref: 'customChart' }).vm, 'prepareCopy')
      .returns(URL.createObjectURL(new Blob()))

    await wrapper.findComponent({ name: 'pivotUi' }).setValue({
      rows: ['item'],
      cols: ['year'],
      colOrder: 'value_a_to_z',
      rowOrder: 'value_z_to_a',
      aggregator: $.pivotUtilities.aggregators.Count(),
      aggregatorName: 'Count',
      renderer: $.pivotUtilities.renderers['Custom chart'],
      rendererName: 'Custom chart',
      vals: []
    })

    expect(await wrapper.vm.prepareCopy()).to.be.a('string')

    await wrapper.findComponent({ name: 'pivotUi' }).setValue({
      rows: ['item'],
      cols: ['year'],
      colOrder: 'value_a_to_z',
      rowOrder: 'value_z_to_a',
      aggregator: $.pivotUtilities.aggregators.Count(),
      aggregatorName: 'Count',
      renderer: $.pivotUtilities.renderers['Bar Chart'],
      rendererName: 'Bar Chart',
      vals: []
    })

    expect(await wrapper.vm.prepareCopy()).to.be.a('string')
  })

  it('saveAsSvg calls chart method if renderer is Custom Chart', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Custom chart'],
          rendererName: 'Custom chart',
          rendererOptions: {
            customChartOptions: {
              data: [],
              layout: {},
              frames: []
            }
          }
        }
      },
      attachTo: container,
      global: {
        mocks: { $store }
      }
    })

    const chartComponent = wrapper.findComponent({ ref: 'customChart' }).vm
    sinon.stub(chartComponent, 'saveAsSvg')

    await wrapper.vm.saveAsSvg()
    expect(chartComponent.saveAsSvg.called).to.equal(true)
  })

  it('saveAsHtml calls chart method if renderer is Custom Chart', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Custom chart'],
          rendererName: 'Custom chart',
          rendererOptions: {
            customChartOptions: {
              data: [],
              layout: {},
              frames: []
            }
          }
        }
      },
      attachTo: container,
      global: {
        mocks: { $store }
      }
    })

    const chartComponent = wrapper.findComponent({ ref: 'customChart' }).vm
    sinon.stub(chartComponent, 'saveAsHtml')

    await wrapper.vm.saveAsHtml()
    expect(chartComponent.saveAsHtml.called).to.equal(true)
  })

  it('saveAsPng calls chart method if renderer is Custom Chart', async () => {
    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Custom chart'],
          rendererName: 'Custom chart',
          rendererOptions: {
            customChartOptions: {
              data: [],
              layout: {},
              frames: []
            }
          }
        }
      },
      attachTo: container,
      global: {
        mocks: { $store }
      }
    })

    const chartComponent = wrapper.findComponent({ ref: 'customChart' }).vm
    sinon.stub(chartComponent, 'saveAsPng')

    await wrapper.vm.saveAsPng()
    expect(chartComponent.saveAsPng.called).to.equal(true)
  })

  it('saveAsSvg - standart chart', async () => {
    sinon.spy(chartHelper, 'getImageDataUrl')

    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Bar Chart'],
          rendererName: 'Bar Chart'
        }
      },
      attachTo: container,
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.vm.saveAsSvg()
    expect(chartHelper.getImageDataUrl.calledOnce).to.equal(true)
  })

  it('saveAsHtml - standart chart', async () => {
    sinon.spy(chartHelper, 'getChartData')
    sinon.spy(chartHelper, 'getHtml')

    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Bar Chart'],
          rendererName: 'Bar Chart'
        }
      },
      attachTo: container,
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.vm.saveAsHtml()
    expect(chartHelper.getChartData.calledOnce).to.equal(true)
    const chartData = await chartHelper.getChartData.returnValues[0]
    expect(chartHelper.getHtml.calledOnceWith(chartData)).to.equal(true)
  })

  it('saveAsHtml - table', async () => {
    sinon.stub(pivotHelper, 'getPivotHtml')
    sinon.stub(fIo, 'exportToFile')

    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers.Table,
          rendererName: 'Table'
        }
      },
      attachTo: container,
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.vm.saveAsHtml()
    expect(pivotHelper.getPivotHtml.calledOnce).to.equal(true)
    const html = pivotHelper.getPivotHtml.returnValues[0]
    expect(
      fIo.exportToFile.calledOnceWith(html, 'pivot.html', 'text/html')
    ).to.equal(true)
  })

  it('saveAsPng - standart chart', async () => {
    sinon.stub(chartHelper, 'getImageDataUrl').returns('standat chart data url')
    sinon.stub(fIo, 'downloadFromUrl')

    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Bar Chart'],
          rendererName: 'Bar Chart'
        }
      },
      attachTo: container,
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.vm.saveAsPng()
    expect(chartHelper.getImageDataUrl.calledOnce).to.equal(true)
    await chartHelper.getImageDataUrl.returnValues[0]
    expect(wrapper.emitted().loadingImageCompleted.length).to.equal(1)
    expect(
      fIo.downloadFromUrl.calledOnceWith('standat chart data url', 'pivot')
    ).to.equal(true)
  })

  it('saveAsPng - table', async () => {
    sinon
      .stub(pivotHelper, 'getPivotCanvas')
      .returns(document.createElement('canvas'))
    sinon
      .stub(HTMLCanvasElement.prototype, 'toDataURL')
      .returns('canvas data url')
    sinon.stub(fIo, 'downloadFromUrl')

    const wrapper = mount(Pivot, {
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers.Table,
          rendererName: 'Table'
        }
      },
      attachTo: container,
      global: {
        stubs: { chart: true }
      }
    })

    await wrapper.vm.saveAsPng()
    expect(HTMLCanvasElement.prototype.toDataURL.calledOnce).to.equal(true)
    await HTMLCanvasElement.prototype.toDataURL.returnValues[0]
    expect(wrapper.emitted().loadingImageCompleted.length).to.equal(1)
    expect(
      fIo.downloadFromUrl.calledOnceWith('canvas data url', 'pivot')
    ).to.equal(true)
  })

  it('resizes standart chart', async () => {
    const wrapper = mount(Pivot, {
      global: {
        mocks: { $store: { state: { isWorkspaceVisible: true } } }
      },
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Bar Chart'],
          rendererName: 'Bar Chart'
        }
      },
      attachTo: container
    })

    await flushPromises()

    const plotContainer = wrapper.find('.pivot-output').wrapperElement
    const plot = wrapper.find('.svg-container').wrapperElement

    const initialContainerWidth = plotContainer.scrollWidth
    const initialContainerHeight = plotContainer.scrollHeight

    const initialPlotWidth = plot.scrollWidth
    const initialPlotHeight = plot.scrollHeight

    const newContainerWidth = initialContainerWidth * 2 || 1000
    const newContainerHeight = initialContainerHeight * 2 || 2000

    plotContainer.style.width = `${newContainerWidth}px`
    plotContainer.style.height = `${newContainerHeight}px`

    await flushPromises()

    const plotAfterResize = wrapper.find('.svg-container').wrapperElement
    expect(plotAfterResize.scrollWidth).not.to.equal(initialPlotWidth)

    expect(plotAfterResize.scrollWidth.scrollHeight).not.to.equal(
      initialPlotHeight
    )

    wrapper.unmount()
  })

  it('hides or shows controlls depending on showViewSettings and resizes standart chart', async () => {
    const wrapper = mount(Pivot, {
      global: {
        mocks: { $store: { state: { isWorkspaceVisible: true } } }
      },
      props: {
        dataSources: {
          item: ['foo', 'bar', 'bar', 'bar'],
          year: [2021, 2021, 2020, 2020]
        },
        initOptions: {
          rows: ['item'],
          cols: ['year'],
          colOrder: 'key_a_to_z',
          rowOrder: 'key_a_to_z',
          aggregatorName: 'Count',
          vals: [],
          renderer: $.pivotUtilities.renderers['Bar Chart'],
          rendererName: 'Bar Chart'
        }
      },
      attachTo: container
    })

    expect(wrapper.find('.pivot-ui').isVisible()).to.equal(false)

    await flushPromises()

    const plot = wrapper.find('.svg-container').wrapperElement
    const initialPlotHeight = plot.scrollHeight

    await wrapper.setProps({ showViewSettings: true })
    expect(wrapper.find('.pivot-ui').isVisible()).to.equal(true)

    await flushPromises()
    const plotAfterResize = wrapper.find('.svg-container').wrapperElement

    expect(plotAfterResize.scrollWidth.scrollHeight).not.to.equal(
      initialPlotHeight
    )

    wrapper.unmount()
  })
})
