'use strict'

const chai = require('chai')
const Serverless = require('../../../../lib/serverless')
const Install = require('../../../../lib/plugins/install.js')
const sinon = require('sinon')
const download = require('../../../../lib/utils/download-template-from-repo')
const fse = require('fs-extra')
const path = require('path')
const { getTmpDirPath } = require('../../../utils/fs')

chai.use(require('sinon-chai'))
const { expect } = require('chai')

describe('Install', () => {
  let install
  let serverless
  let cwd

  let serviceDir

  beforeEach(async () => {
    const tmpDir = getTmpDirPath()
    cwd = process.cwd()

    fse.mkdirsSync(tmpDir)
    process.chdir(tmpDir)

    serviceDir = tmpDir

    serverless = new Serverless({
      commands: ['print'],
      options: {},
      serviceDir: null,
    })
    install = new Install(serverless)
    return serverless.init().then(() => {
      install.serverless.cli = new serverless.classes.CLI()
    })
  })

  afterEach(() => {
    // change back to the old cwd
    process.chdir(cwd)
  })

  describe('#constructor()', () => {
    let installStub

    beforeEach(() => {
      installStub = sinon.stub(install, 'install').resolves()
    })

    afterEach(() => {
      install.install.restore()
    })

    it('should have commands', () => expect(install.commands).to.be.not.empty)

    it('should have hooks', () => expect(install.hooks).to.be.not.empty)

    it('should run promise chain in order for "install:install" hook', async () =>
      install.hooks['install:install']().then(() => {
        expect(installStub.calledOnce).to.be.equal(true)
      }))
  })

  describe('#install()', () => {
    let downloadStub

    beforeEach(() => {
      downloadStub = sinon.stub(download, 'downloadTemplateFromRepo')
    })

    afterEach(() => {
      download.downloadTemplateFromRepo.restore()
    })

    it('should throw an error if the passed URL option is not a valid URL', async () => {
      install.options = { url: 'invalidUrl' }

      try {
        await install.install()
      } catch (e) {
        expect(e).to.be.instanceOf(Error)
      }
    })

    it('should throw an error if the passed URL is not a valid GitHub URL', async () => {
      install.options = { url: 'http://no-github-url.com/foo/bar' }

      try {
        await install.install()
      } catch (e) {
        expect(e).to.be.instanceOf(Error)
      }
    })

    it('should throw an error if a directory with the same service name is already present', async () => {
      install.options = { url: 'https://github.com/johndoe/existing-service' }

      const serviceDirName = path.join(serviceDir, 'existing-service')
      fse.mkdirsSync(serviceDirName)

      try {
        await install.install()
      } catch (e) {
        expect(e).to.be.instanceOf(Error)
      }
    })

    it('should succeed if template can be downloaded and installed', async () => {
      install.options = { url: 'https://github.com/johndoe/remote-service' }
      downloadStub.resolves('remote-service')

      return install.install().then(() => {
        expect(downloadStub).to.have.been.calledOnce
      })
    })

    it('should succeed and print out the desired service name', async () => {
      install.options = { url: 'https://github.com/johndoe/remote-service' }
      install.options.name = 'remote'
      downloadStub.resolves('remote-service')

      return install.install().then(() => {
        expect(downloadStub).to.have.been.calledOnce
      })
    })
  })
})
