// This script uses the Matomo API which requires an access token
// Once you have your access token you can put it in a `.env` file at the root
// of the project to enable it during development. For instance:
//
// MATOMO_TOKEN=f4336c82cb1e494752d06e610614eab12b65f1d1
//
// Matomo API documentation:
// https://developer.matomo.org/api-reference/reporting-api

require('dotenv').config()
require('isomorphic-fetch')
const querystring = require('querystring')
const { createDataDir, writeInDataDir } = require('./utils.js')
const R = require('ramda')

const apiURL = params => {
	const query = querystring.stringify({
		period: 'month',
		date: 'last1',
		method: 'API.get',
		format: 'JSON',
		module: 'API',
		idSite: 39,
		language: 'fr',
		apiAction: 'get',
		token_auth: process.env.MATOMO_TOKEN,
		...params
	})
	return `https://stats.data.gouv.fr/index.php?${query}`
}

async function main() {
	createDataDir()
	const stats = {
		simulators: await fetchSimulatorsMonth(),
		monthlyVisits: await fetchMonthlyVisits(),
		dailyVisits: await fetchDailyVisits(),
		statusChosen: await fetchStatusChosen(),
		feedback: await fetchFeedback(),
		channelType: await fetchChannelType()
	}
	writeInDataDir('stats.json', stats)
}

function xMonthAgo(x = 0) {
	const date = new Date()
	if (date.getMonth() - x > 0) {
		date.setMonth(date.getMonth() - x)
	} else {
		date.setMonth(12 + date.getMonth() - x)
		date.setFullYear(date.getFullYear() - 1)
	}
	return date.toISOString().substring(0, 7)
}

async function fetchSimulatorsMonth() {
	const getDataFromXMonthAgo = async x => {
		const date = xMonthAgo(x)
		return { date, visites: await fetchSimulators(`${date}-01`) }
	}
	return {
		currentMonth: await getDataFromXMonthAgo(0),
		oneMonthAgo: await getDataFromXMonthAgo(1),
		twoMonthAgo: await getDataFromXMonthAgo(2)
	}
}

async function fetchSimulators(dt) {
	try {
		const response = await fetch(
			apiURL({
				period: 'month',
				date: `${dt}`,
				method: 'Actions.getPageUrls',
				filter_limits: -1
			})
		)
		const firstLevelData = await response.json()

		const coronavirusPage = firstLevelData.find(
			page => page.label === '/coronavirus'
		)

		// Visits on simulators pages
		const idSubTableSimulateurs = firstLevelData.find(
			page => page.label === 'simulateurs'
		).idsubdatatable

		const responseSimulateurs = await fetch(
			apiURL({
				date: `${dt}`,
				method: 'Actions.getPageUrls',
				search_recursive: 1,
				filter_limits: -1,
				idSubtable: idSubTableSimulateurs
			})
		)

		const dataSimulateurs = await responseSimulateurs.json()
		const resultSimulateurs = dataSimulateurs
			.filter(({ label }) =>
				[
					'/salarié',
					'/auto-entrepreneur',
					'/artiste-auteur',
					'/indépendant',
					'/comparaison-régimes-sociaux',
					'/assimilé-salarié'
				].includes(label)
			)

			/// Two '/salarié' pages are reported on Matomo, one of which has very few
			/// visitors. We delete it manually.
			.filter(
				x =>
					x.label != '/salarié' ||
					x.nb_visits !=
						dataSimulateurs
							.filter(x => x.label == '/salarié')
							.reduce((a, b) => Math.min(a, b.nb_visits), 1000)
			)

		// Add iframes
		const idTableIframes = firstLevelData.find(page => page.label == 'iframes')
			.idsubdatatable
		const responseIframes = await fetch(
			apiURL({
				date: `${dt}`,
				method: 'Actions.getPageUrls',
				search_recursive: 1,
				filter_limits: -1,
				idSubtable: idTableIframes
			})
		)
		const dataIframes = await responseIframes.json()
		const resultIframes = dataIframes.filter(x =>
			[
				'/simulateur-embauche?couleur=',
				'/simulateur-autoentrepreneur?couleur='
			].includes(x.label)
		)

		const groupSimulateursIframesVisits = ({ label }) =>
			label.startsWith('/simulateur-embauche')
				? '/salarié'
				: label.startsWith('/simulateur-autoentrepreneur')
				? '/auto-entrepreneur'
				: label

		const sumVisits = (acc, { nb_visits }) => acc + nb_visits
		const results = R.reduceBy(
			sumVisits,
			0,
			groupSimulateursIframesVisits,
			[...resultSimulateurs, ...resultIframes, coronavirusPage].filter(
				x => x !== undefined
			)
		)
		return Object.entries(results)
			.map(([label, nb_visits]) => ({ label, nb_visits }))
			.sort((a, b) => b.nb_visits - a.nb_visits)
	} catch (e) {
		console.log('fail to fetch Simulators Visits')
		return null
	}
}

// We had a tracking bug in 2019, in which every click on Safari+iframe counted
// as a visit, so the numbers are manually corrected.
const visitsIn2019 = {
	'2019-01': 119541,
	'2019-02': 99065,
	'2019-03': 122931,
	'2019-04': 113454,
	'2019-05': 118637,
	'2019-06': 152981,
	'2019-07': 141079,
	'2019-08': 127326,
	'2019-09': 178474,
	'2019-10': 198260,
	'2019-11': 174515,
	'2019-12': 116305
}

async function fetchMonthlyVisits() {
	try {
		const response = await fetch(
			apiURL({
				period: 'month',
				date: 'previous12',
				method: 'VisitsSummary.getUniqueVisitors'
			})
		)
		const data = await response.json()
		const result = Object.entries({ ...data, ...visitsIn2019 })
			.sort(([t1], [t2]) => (t1 > t2 ? 1 : -1))
			.map(([date, visiteurs]) => ({ date, visiteurs }))
		return result
	} catch (e) {
		console.log('fail to fetch Monthly Visits')
		return null
	}
}

async function fetchDailyVisits() {
	try {
		const response = await fetch(
			apiURL({
				period: 'day',
				date: 'previous30',
				method: 'VisitsSummary.getUniqueVisitors'
			})
		)
		const data = await response.json()
		return Object.entries(data).map(([date, visiteurs]) => ({
			date,
			visiteurs
		}))
	} catch (e) {
		console.log('fail to fetch Daily Visits')
		return null
	}
}

async function fetchStatusChosen() {
	try {
		const response = await fetch(
			apiURL({
				method: 'Events.getAction',
				label: 'status chosen',
				date: 'previous1'
			})
		)
		const data = await response.json()
		const response2 = await fetch(
			apiURL({
				method: 'Events.getNameFromActionId',
				idSubtable: Object.values(data)[0][0].idsubdatatable,
				date: 'previous1'
			})
		)
		const data2 = await response2.json()
		const result = Object.values(data2)[0].map(({ label, nb_visits }) => ({
			label,
			nb_visits
		}))
		return result
	} catch (e) {
		console.log('fail to fetch Status Chosen')
		return null
	}
}

async function fetchFeedback() {
	try {
		const APIcontent = await fetch(
			apiURL({
				method: 'Events.getCategory',
				label: 'Feedback > @rate%20page%20usefulness',
				date: 'previous5'
			})
		)
		const APIsimulator = await fetch(
			apiURL({
				method: 'Events.getCategory',
				label: 'Feedback > @rate%20simulator',
				date: 'previous5'
			})
		)
		const feedbackcontent = await APIcontent.json()
		const feedbacksimulator = await APIsimulator.json()

		let content = 0
		let simulator = 0
		let j = 0
		// The weights are defined by taking the coefficients of an exponential
		// smoothing with alpha=0.8 and normalizing them. The current month is not
		// considered.
		const weights = [0.0015, 0.0076, 0.0381, 0.1905, 0.7623]
		for (const i in feedbackcontent) {
			content += feedbackcontent[i][0].avg_event_value * weights[j]
			simulator += feedbacksimulator[i][0].avg_event_value * weights[j]
			j += 1
		}
		return {
			content: Math.round(content * 10),
			simulator: Math.round(simulator * 10)
		}
	} catch (e) {
		console.log('fail to fetch feedbacks')
		return null
	}
}

async function fetchChannelType() {
	try {
		const response = await fetch(
			apiURL({
				period: 'month',
				date: 'last3',
				method: 'Referrers.getReferrerType'
			})
		)

		const data = await response.json()

		const result = R.map(
			date =>
				date
					.filter(x =>
						['Sites web', 'Moteurs de recherche', 'Entrées directes'].includes(
							x.label
						)
					)
					.map(({ label, nb_visits }) => ({
						label,
						nb_visits
					})),
			data
		)
		const dates = Object.keys(result).sort((t1, t2) => t1 - t2)
		return {
			currentMonth: { date: dates[0], visites: result[dates[0]] },
			oneMonthAgo: { date: dates[1], visites: result[dates[1]] },
			twoMonthAgo: { date: dates[2], visites: result[dates[2]] }
		}
	} catch (e) {
		console.log('fail to fetch channel type')
		return null
	}
}

main()