import { expect } from 'chai'
import { describe, it, beforeEach, afterEach } from 'mocha'
import { createMemoryHistory } from 'history'
import { DottedName } from 'modele-social'
import { createStore } from 'redux'
import * as sinon from 'sinon'
import {
	loadPreviousSimulation,
	setSimulationConfig,
	updateSituation,
} from '../source/actions/actions'
import reducers, {
	Simulation,
	SimulationConfig,
} from '../source/reducers/rootReducer'
import { setupSimulationPersistence } from '../source/storage/persistSimulation'
import safeLocalStorage from '../source/storage/safeLocalStorage'

function delay(ms: number) {
	return new Promise((resolve) => setTimeout(resolve, ms))
}

const simulationConfig: SimulationConfig = {
	objectifs: [],
	'objectifs cachés': [],
	situation: {},
	'unité par défaut': '€/mois',
}
const initialSimulation: Simulation = {
	config: simulationConfig,
	url: '/someurl',
	hiddenNotifications: [],
	situation: {},
	initialSituation: {},
	targetUnit: '€/mois',
	foldedSteps: ['somestep' as DottedName],
	unfoldedStep: null,
}

describe('[persistence] When simulation persistence is setup', () => {
	const sandbox = sinon.createSandbox()
	let spiedSafeLocalStorage: any
	let store: any

	beforeEach(() => {
		spiedSafeLocalStorage = sandbox.spy(safeLocalStorage as any)
		store = createStore(reducers, {
			simulation: initialSimulation,
			activeTargetInput: 'sometargetinput',
		} as any)

		setupSimulationPersistence(store, 0)
	})
	afterEach(() => {
		sandbox.restore()
	})

	describe('when the state is changed with some data that is persistable', () => {
		beforeEach(async () => {
			store.dispatch(updateSituation('dotted name' as DottedName, '42'))
			await delay(0)
		})
		it('saves state in localStorage with all fields', () => {
			expect(spiedSafeLocalStorage.setItem.calledOnce).to.be.true
			expect(spiedSafeLocalStorage.setItem.getCall(0).args[1]).to.eq(
				'{"situation":{"dotted name":"42"},"activeTargetInput":"sometargetinput","foldedSteps":["somestep"]}'
			)
		})
		it('saves state in localStorage with a key dependent on the simulation url', () => {
			expect(spiedSafeLocalStorage.setItem.calledOnce).to.be.true
			expect(spiedSafeLocalStorage.setItem.getCall(0).args[0]).to.contain(
				'someurl'
			)
		})
	})
})

describe('[persistence] When simulation config is set', () => {
	const serializedPreviousSimulation =
		'{"situation":{"dotted name . other":"42"},"activeTargetInput":"someothertargetinput","foldedSteps":["someotherstep"]}'

	const sandbox = sinon.createSandbox()
	let store: any

	beforeEach(() => {
		sandbox
			.stub(safeLocalStorage, 'getItem')
			.callsFake(() => serializedPreviousSimulation)
		store = createStore(reducers)
		const history = createMemoryHistory()
		history.replace('/someotherurl')

		store.dispatch(
			setSimulationConfig(simulationConfig, history.location.pathname)
		)
	})
	afterEach(() => {
		sandbox.restore()
	})
	describe('when previous simulation is loaded in state', () => {
		beforeEach(() => {
			store.dispatch(loadPreviousSimulation())
		})
		it('loads url in state', () => {
			expect(store.getState().simulation.url).to.eq('/someotherurl')
		})
		it('loads situation in state', () => {
			expect(store.getState().simulation.situation).to.deep.eq({
				'dotted name . other': '42',
			})
		})
		it('loads activeTargetInput in state', () => {
			expect(store.getState().activeTargetInput).to.eq('someothertargetinput')
		})
		it('loads foldedSteps in state', () => {
			expect(store.getState().simulation.foldedSteps).to.deep.eq([
				'someotherstep',
			])
		})
	})
})