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

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
          format: euro
      `,
      rules = parseAll(yaml.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
          format: euro
          formule:
            inversion:
              avec:
                - net
      `,
      rules = parseAll(yaml.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
                  taux: 80%
                - si: ≠ cadre
                  taux: 70%

        - nom: brut
          format: euro
          formule:
            inversion:
              avec:
                - net
        - nom: cadre
        - nom: assiette
          formule: 67 + brut

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

    expect(analysis.targets[0].nodeValue).to.be.null
    expect(missing).to.have.key("brut")
  })

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

        - nom: brut
          format: euro
          formule:
            inversion:
              avec:
                - net
        - nom: cadre
        - nom: assiette
          formule: 67 + brut

      `,
      rules = parseAll(yaml.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.have.key("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
                  taux: 80%
                - si: ≠ cadre
                  taux: 70%

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

        - nom: brut
          format: euro
          formule:
            inversion:
              avec:
                - net
                - total

        - nom: cadre

        - nom: assiette
          formule: 67 + brut

      `,
      rules = parseAll(yaml.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.equal(3750)
    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
        format: euro
        formule:
          inversion:
            avec:
              - net
              - total
    `,
    rules = parseAll(yaml.safeLoad(rawRules).map(enrichRule)),
    stateSelector = name => ({ net: 2000 }[name]),
    analysis = analyse(rules, "total")(stateSelector),
    missing = collectMissingVariables(analysis.targets)

  expect(analysis.targets[0].nodeValue).to.equal(3750)
  expect(missing).to.be.empty
})

it('should collect missing variables not too slowly', function() {
  let stateSelector = (name) => ({"contrat salarié . salaire net":"2300"})[name]

  let rules = parseAll(realRules.map(enrichRule)),
      analysis = analyseMany(rules,["salaire brut","salaire total"])(stateSelector)

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