import groovy.transform.Field

/* SETTINGS */

// in minutes
def sharness_timeout = 15
def gotest_timeout = 10
def build_timeout = 20
def check_timeout = 4

def test = 'go test -v ./...'

def fast_build_platforms = [
  ['linux', 'amd64'],
]

def build_platforms = [
  ['windows', '386'],
  ['windows', 'amd64'],

  ['linux', 'arm'],
  ['linux', 'arm64'],
  ['linux', '386'],

  ['darwin', '386'],
  ['darwin', 'amd64'],

  ['freebsd', 'amd64']
]

/* js-* tests */
def js_api_commit = 'v29.1.0'
def interop_commit = 'master'

def js_api_platforms = ['linux', 'macos']
def js_api_node_versions = ['10.4.1']

def yarn_version = '1.5.1'
def yarn_path = './node_modules/.bin/yarn'


/* PIPELINE UTILS */

def setupStep(nodeLabel, f) {
  node(label: nodeLabel) {
    def ps = nodeLabel != 'windows' ? '/' : '\\'
    def psep = nodeLabel != 'windows' ? ':' : ';'

    def root = tool name: '1.11', type: 'go'
    def jobNameArr = "${JOB_NAME}"
    def jobName = jobNameArr.split("/")[0..1].join(nodeLabel != 'windows' ? '/' : '\\\\').toLowerCase()
    def subName = jobNameArr.split("/")[2].toLowerCase()
    def originalWs = "${WORKSPACE}"

    ws("${originalWs}${ps}src${ps}github.com${ps}${jobName}") {
      def goEnv = ["GOROOT=${root}", "GOPATH=${originalWs}", "SUBNAME=${subName}", "PATH=$PATH${psep}${root}${ps}bin${psep}${originalWs}${ps}bin"]
      withEnv(goEnv) {
        checkout scm

        def run = nodeLabel != 'windows' ? this.&sh : this.&bat

        try {
          f(run)
        } finally {
          cleanWs()
        }
      }
    }
  }
}

def gobuild_step = { list ->
  setupStep('linux') { run ->
    timeout(time: build_timeout, unit: 'MINUTES') {
      run "make gx-deps"

      list.each { platform ->
        timeout(time: check_timeout, unit: 'MINUTES') {
          withEnv(["GOOS=${platform[0]}", "GOARCH=${platform[1]}"]) {
            run "go build -i -ldflags=\"-X github.com/ipfs/go-ipfs/repo/config.CurrentCommit=${env.SUBNAME}-${env.BUILD_NUMBER}\"  -o cmd/ipfs/ipfs github.com/ipfs/go-ipfs/cmd/ipfs"
            run "cp cmd/ipfs/ipfs cmd/ipfs/dist; cd cmd/ipfs/dist; tar -czvf ../go-ipfs_${env.GOOS}-${env.GOARCH}-${env.SUBNAME}-${env.BUILD_NUMBER}.tar.gz ."
            archiveArtifacts artifacts: "cmd/ipfs/go-ipfs_${env.GOOS}-${env.GOARCH}-${env.SUBNAME}-${env.BUILD_NUMBER}.tar.gz", fingerprint: true
            stash name: "ipfs-${env.GOOS}-${env.GOARCH}", includes: "cmd/ipfs/ipfs"
          }
        }
      }
    }
  }
}

def sharness_step = { run, osname, makeargs, ignore ->
  timeout(time: sharness_timeout, unit: 'MINUTES') {
    run "make gx-deps"

    try {
      run "make -j12 ${makeargs} test/sharness/test-results/sharness.xml CONTINUE_ON_S_FAILURE=1 TEST_NO_FUSE=1 TEST_NO_DOCKER=1"

      try {
        // archive trash directories if any
        run "tar -czf sharnessTrashDirs-${osname}.tar.gz test/sharness/trash\\ *"
        archiveArtifacts artifacts: "sharnessTrashDirs-${osname}.tar.gz", fingerprint: true
      } catch (_) {}
    } catch (err) {
      throw err
    } finally {
      if (!ignore) {
        junit allowEmptyResults: true, testResults: 'test/sharness/test-results/sharness.xml'
      }
    }
  }
}

/* JS BASED TESTS */

def jsTestStep = { os, nodeVer, repo, commit ->
  return { ->
    setupStep(os) {
      switch (os) {
        case "linux":
          unstash("ipfs-linux-amd64")
          break
        case "macos":
          unstash("ipfs-darwin-amd64")
          break
      }

      withEnv(['IPFS_GO_EXEC=../cmd/ipfs/ipfs', 'CI=true']) {
        dir('.js-test') {
          checkout changelog: false, scm: [$class: 'GitSCM', branches: [[name: commit]], userRemoteConfigs: [[url: repo]]]

          fileExists 'package.json'
          nodejs(nodeVer) {
            sh 'rm -rf node_modules/'
            sh 'npm install yarn@' + yarn_version
            sh yarn_path + ' --mutex network --no-lockfile'
            def runTest = { ->
              try {
                sh yarn_path + ' test'
              } catch(_) {
              } finally {
                junit allowEmptyResults: true, testResults: 'junit-report-*.xml'
                cleanWs()
              }
            }

            if (os == "linux") {
              wrap([$class: 'Xvfb', parallelBuild: true, autoDisplayName: true]) {
                runTest()
              }
            } else {
              runTest()
            }
          }
        }
      }
    }
  }
}

/* PIPELINE */

def reportedStage(name, fn) {
  githubNotify description: "Running ${name}", status: 'PENDING', context: "continuous-integration/jenkins/${name}"

  try {
    stage(name.capitalize()) {
      fn()
    }
    githubNotify description: "${name.capitalize()} passed", status: 'SUCCESS', context: "continuous-integration/jenkins/${name}"
  } catch (err) {
    githubNotify description: "${name.capitalize()} failed", status: 'FAILURE', context: "continuous-integration/jenkins/${name}"
    throw err
  }

}

ansiColor('xterm') { withEnv(['TERM=xterm-color']) {
  reportedStage('checks') {
    parallel(
      'go fmt': {
        setupStep('linux') { run ->
          timeout(time: check_timeout, unit: 'MINUTES') {
            run 'make test_go_fmt'
          }
        }
      },
      'go vet': {
        setupStep('linux') { run ->
          timeout(time: check_timeout, unit: 'MINUTES') {
            run 'make gx-deps'
            run 'go vet ./...'
          }
        }
      },
      'go build': {
        gobuild_step(fast_build_platforms)
      },
      'gx deps dupes': {
        setupStep('linux') { run ->
          timeout(time: check_timeout, unit: 'MINUTES') {
            run 'make gx-deps'
            run 'test -z "$(./bin/gx deps dupes)"'
          }
        }
      }
    )
  }


  reportedStage('tests') {
    parallel(
      'go build (other platforms)': {
        gobuild_step(build_platforms)
      },
      windows: {
        setupStep('windows') { run ->
          timeout(time: gotest_timeout, unit: 'MINUTES') {
            run 'go get -v github.com/jstemmer/go-junit-report github.com/whyrusleeping/gx github.com/whyrusleeping/gx-go'
            run "gx install --global"

            try {
              run test + ' -tags="nofuse" > output & type output'
              run 'type output | go-junit-report > junit-report-windows.xml'
            } catch (err) {
              throw err
            } finally {
              /* IGNORE TEST FAILS */
              /* junit allowEmptyResults: true, testResults: 'junit-report-*.xml' */
            }
          }
        }
      },
      linux: {
        setupStep('linux') { run ->
          timeout(time: gotest_timeout, unit: 'MINUTES') {
            run 'go get -v github.com/jstemmer/go-junit-report'
            run "make gx-deps"

            try {
              run test + ' -tags="nofuse" 2>&1 | tee output'
              run 'cat output | go-junit-report > junit-report-linux.xml'
            } catch (err) {
              throw err
            } finally {
              junit allowEmptyResults: true, testResults: 'junit-report-*.xml'
            }
          }
        }
      },
      linuxSharness: {
       setupStep('linux') { run ->
         sharness_step(run, 'linux', '-Otarget', false)
        }
      },
      //macOS: {
      //  setupStep('macos') { run ->
      //    sharness_step(run, 'macos', '', true)
      //  }
      //},
      //macSharness: {
      //  setupStep('macos') { run ->
      //    timeout(time: sharness_timeout, unit: 'MINUTES') {
      //      run 'go get -v github.com/jstemmer/go-junit-report'
      //      run "make gx-deps"

      //      try {
      //        run "make -j12 test/sharness/test-results/sharness.xml CONTINUE_ON_S_FAILURE=1 TEST_NO_FUSE=1"
      //      } catch (err) {
      //        throw err
      //      } finally {
      //        /* IGNORE TEST FAILS */
      //        /* junit allowEmptyResults: true, testResults: 'test/sharness/test-results/sharness.xml' */
      //      }
      //    }
      //  }
      //},
    )
  }

  reportedStage('interop/apis') {
    def steps = [:]
    js_api_platforms.each {os ->
      js_api_node_versions.each { nodeVer ->
        steps["interop-${os}-${nodeVer}"] = jsTestStep(os, nodeVer, 'https://github.com/ipfs/interop', interop_commit)
        steps["js-api-${os}-${nodeVer}"] = jsTestStep(os, nodeVer, 'https://github.com/ipfs/js-ipfs-api', js_api_commit)
      }
    }

    parallel steps
  }

}}
