import { expect } from 'chai'
import dedent from 'dedent-js'
import { safeLoad } from 'js-yaml'
import { collectMissingVariables } from '../source/engine/generateQuestions'
import { enrichRule, rules as realRules } from '../source/engine/rules'
import { analyse, analyseMany, parseAll } from '../source/engine/traverse'

describe('inversions', () => {
	it('should handle non inverted example', () => {
		let fakeState = { brut: 2300 }
		let stateSelector = name => fakeState[name]

		let rawRules = dedent`
        - nom: net
          formule:
            multiplication:
              assiette: brut
              taux: 77%

        - nom: brut
          unité: €
      `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			analysis = analyse(rules, 'net')(stateSelector)

		expect(analysis.targets[0].nodeValue).to.be.closeTo(1771, 0.001)
	})

	it('should handle simple inversion', () => {
		let fakeState = { net: 2000 }
		let stateSelector = name => fakeState[name]

		let rawRules = dedent`
        - nom: net
          formule:
            multiplication:
              assiette: brut
              taux: 77%

        - nom: brut
          unité: €
          formule:
            inversion numérique:
              avec:
                - net
      `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			analysis = analyse(rules, 'brut')(stateSelector)

		expect(analysis.targets[0].nodeValue).to.be.closeTo(
			2000 / (77 / 100),
			0.0001 * 2000
		)
	})

	it('should ask the input of one of the possible inversions', () => {
		let rawRules = dedent`
        - nom: net
          formule:
            multiplication:
              assiette: assiette
              variations:
                - si: cadre
                  alors:
                    taux: 80%
                - sinon:
                    taux: 70%

        - nom: brut
          unité: €
          formule:
            inversion numérique:
              avec:
                - net
        - nom: cadre
        - nom: assiette
          formule: 67 + brut

      `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			stateSelector = () => null,
			analysis = analyse(rules, 'brut')(stateSelector),
			missing = collectMissingVariables(analysis.targets)

		expect(analysis.targets[0].nodeValue).to.be.null
		expect(missing).to.include('brut')
	})

	it('should handle inversions with missing variables', () => {
		let rawRules = dedent`
        - nom: net
          formule:
            multiplication:
              assiette: assiette
              variations:
                - si: cadre
                  alors:
                    taux: 80%
                - sinon:
                    taux: 70%

        - nom: brut
          unité: €
          formule:
            inversion numérique:
              avec:
                - net
        - nom: cadre
        - nom: assiette
          formule:
            somme:
              - 1200
              - brut
              - taxeOne
        - nom: taxeOne
          non applicable si: cadre
          formule: taxe + taxe
        - nom: taxe
          formule:
            multiplication:
              assiette: 1200
              variations:
                - si: cadre
                  alors:
                    taux: 80%
                - sinon:
                    taux: 70%
      `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			stateSelector = name => ({ net: 2000 }[name]),
			analysis = analyse(rules, 'brut')(stateSelector),
			missing = collectMissingVariables(analysis.targets)

		expect(analysis.targets[0].nodeValue).to.be.null
		expect(missing).to.include('cadre')
	})

	it("shouldn't report a missing salary if another salary was input", () => {
		let rawRules = dedent`
        - nom: net
          formule:
            multiplication:
              assiette: assiette
              variations:
                - si: cadre
                  alors:
                    taux: 80%
                - si: cadre != oui
                  alors:
                    taux: 70%

        - nom: total
          formule:
            multiplication:
              assiette: assiette
              taux: 150%

        - nom: brut
          unité: €
          formule:
            inversion numérique:
              avec:
                - net
                - total

        - nom: cadre

        - nom: assiette
          formule: 67 + brut

      `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			stateSelector = name => ({ net: 2000, cadre: 'oui' }[name]),
			analysis = analyse(rules, 'total')(stateSelector),
			missing = collectMissingVariables(analysis.targets)

		expect(analysis.targets[0].nodeValue).to.be.closeTo(3750, 1)
		expect(missing).to.be.empty
	})
	it('complex inversion with composantes', () => {
		let rawRules = dedent`
      - nom: net
        formule:
          multiplication:
            assiette: 67 + brut
            taux: 80%

      - nom: cotisation
        formule:
          multiplication:
            assiette: 67 + brut
            composantes:
              - attributs:
                  dû par: employeur
                taux: 100%
              - attributs:
                  dû par: salarié
                taux: 50%

      - nom: total
        formule: cotisation [employeur] + cotisation [salarié]

      - nom: brut
        unité: €
        formule:
          inversion numérique:
            avec:
              - net
              - total
    `,
			rules = parseAll(safeLoad(rawRules).map(enrichRule)),
			stateSelector = name => ({ net: 2000 }[name]),
			analysis = analyse(rules, 'total')(stateSelector),
			missing = collectMissingVariables(analysis.targets)

		expect(analysis.targets[0].nodeValue).to.be.closeTo(3750, 1)
		expect(missing).to.be.empty
	})
	it('should collect missing variables not too slowly', function() {
		let stateSelector = name =>
			({ 'contrat salarié . rémunération . net': '2300' }[name])

		let rules = parseAll(realRules.map(enrichRule)),
			analysis = analyseMany(rules, [
				'contrat salarié . rémunération . brut',
				'contrat salarié . rémunération . total'
			])(stateSelector)

		let start = Date.now()
		collectMissingVariables(analysis.targets)
		let elapsed = Date.now() - start
		expect(elapsed).to.be.below(1500)
	})
})