var yo = require('yo-yo')
var async = require('async')
var tooltip = require('../ui/tooltip')
var css = require('./styles/test-tab-styles')
var remixTests = require('remix-tests')
import { BaseApi } from 'remix-plugin'

const TestTabLogic = require('./testTab/testTab')

const profile = {
  name: 'solidityUnitTesting',
  displayName: 'Solidity unit testing',
  methods: [],
  events: [],
  icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wUDEhQZ0zbrmQAAAfNJREFUWMPF17lrFVEUx/EPaIKovfAScSndjUtULFQSYhHF0r/Dwsa/RywUiTaWgvaChWsiKkSMZte4o7G5A49x7r0zLy/PA6eZOef3PXebuYfu2xCmcQ9b9NgOYw6rwR9ia6/gR7HQBi/8PjavN/w4FivghV9bT/gwlhLwHzjTVPQ8rqAvE3ciA/+O8abwy/gVBG4lijiJ5czIL64FXvhNbCzFnaoBv9AUPo7fEcEb2BDiTuNTAv4NYxX6u/EIM7GZuZoQXcX1sJk+J2K+YrRCexfetsX9xKVyUB9uZ4r4k3j3BSMR+JvIMv2zQfsxkSkiBj9XAd8ZgRf+vmop+nGnAXwlcs534HUm93FsQ9YtIjby7XiVyZ3BntSpyBWxgrMR+FQG/gF76xzNftxtMO1rgo+G5AdBqLBN4d9eCCyHD1En8Oi0j4UPSBE4hcFSERN4Fz7BZRvEZKcjHynBC5/EQI1lGqgJ3xcTmE4kvswUMRBiUvCPKTg8zQi8QKsirxXe5eD7c1N4ALMZoeelIlrhWSpnNmjXsoM1iihmYhueZGIXcKTp7/hQ6UZb5c+Cp2LmglZHVqeIlC+G2/GarNMiFnGsWzfdpkV0Fd7e5czXgC+FvmDdWq35/wVvbzbnI/DhXvV9Q6W+r6fw9hZsKnjX4H8B0Aamri7CrBsAAAAASUVORK5CYII=',
  description: 'Fast tool to generate unit tests for your contracts',
  location: 'sidePanel',
  documentation: 'unittesting.html'
}

module.exports = class TestTab extends BaseApi {
  constructor (fileManager, filePanel, compileTab, appStore) {
    super(profile)
    this.compileTab = compileTab
    this._view = { el: null }
    this.compileTab = compileTab
    this.fileManager = fileManager
    this.filePanel = filePanel
    this.appStore = appStore
    this.testTabLogic = new TestTabLogic(fileManager)
    this.data = {}
    this.appStore.event.on('activate', (name) => {
      if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
    })
    this.appStore.event.on('deactivate', (name) => {
      if (name === 'solidity') this.updateRunAction(fileManager.currentFile())
    })
  }

  activate () {
    this.listenToEvents()
  }

  deactivate () {
  }

  listenToEvents () {
    this.filePanel.event.register('newTestFileCreated', file => {
      var testList = this.view.querySelector("[class^='testList']")
      var test = yo`<label class="singleTestLabel"><input class="singleTest" onchange=${(e) => this.toggleCheckbox(e.target.checked, file)} type="checkbox" checked="true">${file}</label>`
      testList.appendChild(test)
      this.data.allTests.push(file)
      this.data.selectedTests.push(file)
    })

    this.fileManager.events.on('noFileSelected', () => {
      this.updateGenerateFileAction()
      this.updateRunAction()
      this.updateTestFileList()
    })

    this.fileManager.events.on('currentFileChanged', (file, provider) => {
      this.updateGenerateFileAction(file)
      this.updateRunAction(file)
      this.testTabLogic.getTests((error, tests) => {
        if (error) return tooltip(error)
        this.data.allTests = tests
        this.data.selectedTests = [...this.data.allTests]
        this.updateTestFileList(tests)
        if (!this.testsOutput || !this.testsSummary) return
      })
    })
  }

  listTests () {
    return this.data.allTests.map(test => yo`<label class="singleTestLabel"><input class="singleTest" onchange =${(e) => this.toggleCheckbox(e.target.checked, test)} type="checkbox" checked="true">${test} </label>`)
  }

  toggleCheckbox (eChecked, test) {
    if (!this.data.selectedTests) {
      this.data.selectedTests = this._view.el.querySelectorAll('.singleTest:checked')
    }
    let selectedTests = this.data.selectedTests
    selectedTests = eChecked ? [...selectedTests, test] : selectedTests.filter(el => el !== test)
    this.data.selectedTests = selectedTests
    let checkAll = this._view.el.querySelector('[id="checkAllTests"]')
    if (eChecked) {
      checkAll.checked = true
    } else if (!selectedTests.length) {
      checkAll.checked = false
    }
  }

  checkAll (event) {
    let checkBoxes = this._view.el.querySelectorAll('.singleTest')
    const checkboxesLabels = this._view.el.querySelectorAll('.singleTestLabel')
    // checks/unchecks all
    for (let i = 0; i < checkBoxes.length; i++) {
      checkBoxes[i].checked = event.target.checked
      this.toggleCheckbox(event.target.checked, checkboxesLabels[i].innerText)
    }
  }

  testCallback (result) {
    this.testsOutput.hidden = false
    if (result.type === 'contract') {
      this.testsOutput.appendChild(yo`<div class=${css.outputTitle}>${result.filename} (${result.value})</div>`)
    } else if (result.type === 'testPass') {
      this.testsOutput.appendChild(yo`<div class="${css.testPass} ${css.testLog} bg-success">✓ (${result.value})</div>`)
    } else if (result.type === 'testFailure') {
      this.testsOutput.appendChild(yo`<div class="${css.testFailure} ${css.testLog} bg-danger">✘ (${result.value})</div>`)
    }
  }

  resultsCallback (_err, result, cb) {
    // total stats for the test
    // result.passingNum
    // result.failureNum
    // result.timePassed
    cb()
  }

  updateFinalResult (_err, result, filename) {
    this.testsSummary.hidden = false
    if (_err) {
      this.testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${_err.message}</div>`)
      return
    }
    this.testsSummary.appendChild(yo`<div class=${css.summaryTitle}> ${filename} </div>`)
    if (result.totalPassing > 0) {
      this.testsSummary.appendChild(yo`<div class="text-success" >${result.totalPassing} passing (${result.totalTime}s)</div>`)
      this.testsSummary.appendChild(yo`<br>`)
    }
    if (result.totalFailing > 0) {
      this.testsSummary.appendChild(yo`<div class="text-danger" >${result.totalFailing} failing</div>`)
      this.testsSummary.appendChild(yo`<br>`)
    }
    result.errors.forEach((error, index) => {
      this.testsSummary.appendChild(yo`<div class="text-danger" >${error.context} - ${error.value} </div>`)
      this.testsSummary.appendChild(yo`<div class="${css.testFailureSummary} text-danger" >${error.message}</div>`)
      this.testsSummary.appendChild(yo`<br>`)
    })
  }

  runTest (testFilePath, callback) {
    this.loading.hidden = false
    this.fileManager.fileProviderOf(testFilePath).get(testFilePath, (error, content) => {
      if (error) return
      var runningTest = {}
      runningTest[testFilePath] = { content }
      remixTests.runTestSources(runningTest, (result) => { this.testCallback(result) }, (_err, result, cb) => { this.resultsCallback(_err, result, cb) }, (error, result) => {
        this.updateFinalResult(error, result, testFilePath)
        this.loading.hidden = true
        callback(error)
      }, (url, cb) => {
        return this.compileTab.compileTabLogic.importFileCb(url, cb)
      })
    })
  }

  runTests () {
    this.testsOutput.innerHTML = ''
    this.testsSummary.innerHTML = ''
    var tests = this.data.selectedTests
    if (!tests) return
    this.loading.hidden = tests.length === 0
    async.eachOfSeries(tests, (value, key, callback) => { this.runTest(value, callback) })
  }

  updateGenerateFileAction (currentFile) {
    let el = yo`<button class="${css.generateTestFile} btn btn-primary" onclick="${this.testTabLogic.generateTestFile.bind(this.testTabLogic)}">Generate test file</button>`
    if (!currentFile) {
      el.setAttribute('disabled', 'disabled')
      el.setAttribute('title', 'No file selected')
    }
    if (!this.generateFileActionElement) {
      this.generateFileActionElement = el
    } else {
      yo.update(this.generateFileActionElement, el)
    }
    return this.generateFileActionElement
  }

  updateRunAction (currentFile) {
    let el = yo`<button class="${css.runButton} btn btn-primary"  onclick="${this.runTests.bind(this)}">Run Tests</button>`
    const isSolidityActive = this.appStore.isActive('solidity')
    if (!currentFile || !isSolidityActive) {
      el.setAttribute('disabled', 'disabled')
      if (!currentFile) el.setAttribute('title', 'No file selected')
      if (!isSolidityActive) el.setAttribute('title', 'The solidity module should be activated')
    }

    if (!this.runActionElement) {
      this.runActionElement = el
    } else {
      yo.update(this.runActionElement, el)
    }
    return this.runActionElement
  }

  updateTestFileList (tests) {
    const testsMessage = (tests && tests.length ? this.listTests() : 'No test file available')
    let el = yo`<div class=${css.testList}>${testsMessage}</div>`
    if (!this.testFilesListElement) {
      this.testFilesListElement = el
    } else {
      yo.update(this.testFilesListElement, el)
    }
    return this.testFilesListElement
  }
  render () {
    this.testsOutput = yo`<div class="${css.container} m-3 border border-primary border-right-0 border-left-0 border-bottom-0"  hidden='true' id="tests"></div>`
    this.testsSummary = yo`<div class="${css.container} border border-primary border-right-0 border-left-0 border-bottom-0" hidden='true' id="tests"></div>`
    this.loading = yo`<span class='text-info ml-1'>Running tests...</span>`
    this.loading.hidden = true
    var el = yo`
      <div class="${css.testTabView} card" id="testView">
        <div class="${css.infoBox}">
          Test your smart contract by creating a foo_test.sol file (open ballot_test.sol to see the example).
          <br/>
          You will find more informations in the <a href="https://remix.readthedocs.io/en/latest/unittesting_tab.html">documentation</a>
          Then use the stand alone NPM module remix-tests to run unit tests in your Continuous Integration
          <a href="https://www.npmjs.com/package/remix-tests">https://www.npmjs.com/package/remix-tests</a>.
          <br/>
          For more details, see
          How to test smart contracts guide in our documentation.
          <br/>
          ${this.updateGenerateFileAction()}
        </div>
        <div class="${css.tests}">          
          <div class="${css.buttons}">
          ${this.updateRunAction()}
            <label class="${css.label} mx-4 m-2" for="checkAllTests">
              <input id="checkAllTests"
                type="checkbox"
                onclick="${(event) => { this.checkAll(event) }}"
                checked="true"
              >
              Check/Uncheck all
            </label>
          </div>
          ${this.updateTestFileList()}
          <hr>
          <div class="${css.buttons}" ><h6>Results:${this.loading}</h6></div>
          ${this.testsOutput}
          ${this.testsSummary}
        </div>
      </div>
    `
    if (!this._view.el) this._view.el = el
    return el
  }

}
