sinon = require('sinon')
chai = require('chai')
should = chai.should()
expect = chai.expect
modulePath = '../../../../app/js/Features/Exports/ExportsHandler.js'
SandboxedModule = require('sandboxed-module')

describe 'ExportsHandler', ->

	beforeEach ->
		@ProjectGetter = {}
		@ProjectLocator = {}
		@UserGetter = {}
		@settings = {}
		@stubRequest = {}
		@request = defaults: => return @stubRequest
		@ExportsHandler = SandboxedModule.require modulePath, requires:
			'logger-sharelatex':
				log: ->
				err: ->
			'../Project/ProjectGetter': @ProjectGetter
			'../Project/ProjectLocator': @ProjectLocator
			'../User/UserGetter': @UserGetter
			'settings-sharelatex': @settings
			'request': @request
		@project_id = "project-id-123"
		@project_history_id = 987
		@user_id = "user-id-456"
		@brand_variation_id = 789
		@export_params = {
			project_id: @project_id,
			brand_variation_id: @brand_variation_id,
			user_id: @user_id
		}
		@callback = sinon.stub()

	describe 'exportProject', ->
		beforeEach (done) ->
			@export_data = {iAmAnExport: true}
			@response_body = {iAmAResponseBody: true}
			@ExportsHandler._buildExport = sinon.stub().yields(null, @export_data)
			@ExportsHandler._requestExport = sinon.stub().yields(null, @response_body)
			@ExportsHandler.exportProject @export_params, (error, export_data) =>
				@callback(error, export_data)
				done()

		it "should build the export", ->
			@ExportsHandler._buildExport
			.calledWith(@export_params)
			.should.equal true

		it "should request the export", ->
			@ExportsHandler._requestExport
			.calledWith(@export_data)
			.should.equal true

		it "should return the export", ->
			@callback
			.calledWith(null, @export_data)
			.should.equal true

	describe '_buildExport', ->
		beforeEach (done) ->
			@project =
				id: @project_id
				overleaf:
					history:
						id: @project_history_id
			@user =
				id: @user_id
				first_name: 'Arthur'
				last_name: 'Author'
				email: 'arthur.author@arthurauthoring.org'
			@rootDocPath = 'main.tex'
			@historyVersion = 777
			@ProjectGetter.getProject = sinon.stub().yields(null, @project)
			@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'main.tex'}])
			@UserGetter.getUser = sinon.stub().yields(null, @user)
			@ExportsHandler._requestVersion = sinon.stub().yields(null, @historyVersion)
			done()

		describe "when all goes well", ->
			beforeEach (done) ->
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should request the project history version", ->
				@ExportsHandler._requestVersion.called
				.should.equal true

			it "should return export data", ->
				expected_export_data = 
					project:
						id: @project_id
						rootDocPath: @rootDocPath
						historyId: @project_history_id
						historyVersion: @historyVersion
					user:
						id: @user_id
						firstName: @user.first_name
						lastName: @user.last_name
						email: @user.email
						orcidId: null
					destination:
						brandVariationId: @brand_variation_id
					options:
						callbackUrl: null
				@callback.calledWith(null, expected_export_data)
				.should.equal true

		describe "when we send replacement user first and last name", ->
			beforeEach (done) ->
				@custom_first_name = "FIRST"
				@custom_last_name = "LAST"
				@export_params.first_name = @custom_first_name
				@export_params.last_name = @custom_last_name
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should send the data from the user input", ->
				expected_export_data =
					project:
						id: @project_id
						rootDocPath: @rootDocPath
						historyId: @project_history_id
						historyVersion: @historyVersion
					user:
						id: @user_id
						firstName: @custom_first_name
						lastName: @custom_last_name
						email: @user.email
						orcidId: null
					destination:
						brandVariationId: @brand_variation_id
					options:
						callbackUrl: null
				@callback.calledWith(null, expected_export_data)
				.should.equal true

		describe "when project is not found", ->
			beforeEach (done) ->
				@ProjectGetter.getProject = sinon.stub().yields(new Error("project not found"))
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should return an error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

		describe "when project has no root doc", ->
			beforeEach (done) ->
				@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, null])
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should return an error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

		describe "when user is not found", ->
			beforeEach (done) ->
				@UserGetter.getUser = sinon.stub().yields(new Error("user not found"))
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should return an error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

		describe "when project history request fails", ->
			beforeEach (done) ->
				@ExportsHandler._requestVersion = sinon.stub().yields(new Error("project history call failed"))
				@ExportsHandler._buildExport @export_params, (error, export_data) =>
					@callback(error, export_data)
					done()

			it "should return an error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

	describe '_requestExport', ->
		beforeEach (done) ->
			@settings.apis =
				v1:
					url: 'http://localhost:5000'
					user: 'overleaf'
					pass: 'pass'
			@export_data = {iAmAnExport: true}
			@export_id = 4096
			@stubPost = sinon.stub().yields(null, {statusCode: 200}, { exportId: @export_id })
			done()

		describe "when all goes well", ->
			beforeEach (done) ->
				@stubRequest.post = @stubPost
				@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
					@callback(error, export_v1_id)
					done()

			it 'should issue the request', ->
				expect(@stubPost.getCall(0).args[0]).to.deep.equal
					url: @settings.apis.v1.url + '/api/v1/sharelatex/exports'
					auth:
						user: @settings.apis.v1.user
						pass: @settings.apis.v1.pass
					json: @export_data

			it 'should return the v1 export id', ->
				@callback.calledWith(null, @export_id)
				.should.equal true

		describe "when the request fails", ->
			beforeEach (done) ->
				@stubRequest.post = sinon.stub().yields(new Error("export request failed"))
				@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
					@callback(error, export_v1_id)
					done()

			it "should return an error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

		describe "when the request returns an error code", ->
			beforeEach (done) ->
				@stubRequest.post = sinon.stub().yields(null, {statusCode: 401}, { })
				@ExportsHandler._requestExport @export_data, (error, export_v1_id) =>
					@callback(error, export_v1_id)
					done()

			it "should return the error", ->
				(@callback.args[0][0] instanceof Error)
				.should.equal true

	describe 'fetchExport', ->
		beforeEach (done) ->
			@settings.apis =
				v1:
					url: 'http://localhost:5000'
					user: 'overleaf'
					pass: 'pass'
			@export_id = 897
			@body = "{\"id\":897, \"status_summary\":\"completed\"}"
			@stubGet = sinon.stub().yields(null, {statusCode: 200}, { body: @body })
			done()

		describe "when all goes well", ->
			beforeEach (done) ->
				@stubRequest.get = @stubGet
				@ExportsHandler.fetchExport @export_id, (error, body) =>
					@callback(error, body)
					done()

			it 'should issue the request', ->
				expect(@stubGet.getCall(0).args[0]).to.deep.equal
					url: @settings.apis.v1.url + '/api/v1/sharelatex/exports/' + @export_id
					auth:
						user: @settings.apis.v1.user
						pass: @settings.apis.v1.pass

			it 'should return the v1 export id', ->
				@callback.calledWith(null, { body: @body })
				.should.equal true

