import {Module, Step} from "@/interfaces";
import {Stat, User} from "@/interfaces/user";

type Type = "academic" | "general";

export const writingReverseMarking: {[key: number]: number} = {
	9: 90,
	8.5: 85,
	8: 80,
	7.5: 75,
	7: 70,
	6.5: 65,
	6: 60,
	5.5: 55,
	5: 50,
	4.5: 45,
	4: 40,
	3.5: 35,
	3: 30,
	2.5: 25,
	2: 20,
	1.5: 15,
	1: 10,
	0.5: 5,
	0: 0,
};

export const speakingReverseMarking: {[key: number]: number} = {
	9: 90,
	8.5: 85,
	8: 80,
	7.5: 75,
	7: 70,
	6.5: 65,
	6: 60,
	5.5: 55,
	5: 50,
	4.5: 45,
	4: 40,
	3.5: 35,
	3: 30,
	2.5: 25,
	2: 20,
	1.5: 15,
	1: 10,
	0.5: 5,
	0: 0,
};

export const writingMarking: {[key: number]: number} = {
	90: 9,
	80: 8,
	70: 7,
	60: 6,
	50: 5,
	40: 4,
	30: 3,
	20: 2,
	10: 1,
	0: 0,
};

const readingGeneralMarking: {[key: number]: number} = {
	100: 9,
	97.5: 8.5,
	92.5: 8,
	90: 7.5,
	85: 7,
	80: 6.5,
	75: 6,
	67.5: 5.5,
	57.5: 5,
	45.5: 4.5,
	37.5: 4,
	30: 3.5,
	22.5: 3,
	15: 2.5,
};

const academicMarking: {[key: number]: number} = {
	97.5: 9,
	92.5: 8.5,
	87.5: 8,
	80: 7.5,
	75: 7,
	65: 6.5,
	57.5: 6,
	45: 5.5,
	40: 5,
	32.5: 4.5,
	25: 4,
	20: 3.5,
	15: 3,
	10: 2.5,
};

const levelMarking: {[key: number]: number} = {
	88: 9, // Advanced
	64: 8, // Upper-Intermediate
	52: 6, // Intermediate
	32: 4, // Pre-Intermediate
	16: 2, // Elementary
	0: 0, // Beginner
};

const moduleMarkings: {[key in Module | "overall"]: {[key in Type]: {[key: number]: number}}} = {
	reading: {
		academic: academicMarking,
		general: readingGeneralMarking,
	},
	listening: {
		academic: academicMarking,
		general: academicMarking,
	},
	writing: {
		academic: writingMarking,
		general: writingMarking,
	},
	speaking: {
		academic: writingMarking,
		general: writingMarking,
	},
	level: {
		academic: levelMarking,
		general: levelMarking,
	},
	overall: {
		academic: levelMarking,
		general: levelMarking,
	},
};

export const calculateBandScore = (correct: number, total: number, module: Module | "overall", type: Type) => {
	const marking = moduleMarkings[module][type];
	const percentage = (correct * 100) / total;

	if (module === "level") return percentage;

	for (const value of Object.keys(marking)
		.map((x) => parseFloat(x))
		.sort((a, b) => b - a)) {
		if (percentage >= value) {
			return marking[value];
		}
	}

	return 0;
};

export const calculateAverageLevel = (levels: {[key in Module]: number}) => {
	return (
		Object.keys(levels)
			.filter((x) => x !== "level")
			.reduce((accumulator, current) => levels[current as Module] + accumulator, 0) / 4
	);
};

export const getLevelScore = (level: number) => {
	switch (level) {
		case 0:
			return ["Beginner", "Low A1"];
		case 2:
			return ["Elementary", "High A1/Low A2"];
		case 4:
			return ["Pre-Intermediate", "High A2/Low B1"];
		case 6:
			return ["Intermediate", "High B1/Low B2"];
		case 8:
			return ["Upper-Intermediate", "High B2/Low C1"];
		case 9:
			return ["Advanced", "C1"];
		default:
			return [];
	}
};

export const getLevelLabel = (level: number) => {
	if (level < 2) return ["Foundation", "Pre-A1"];
	if (level < 4) return ["Elementary", "A1"];
	if (level < 5) return ["Pre-intermediate", "A2"];
	if (level < 6) return ["Intermediate", "B1"];
	if (level < 7) return ["Upper Intermediate", "B2"];
	if (level < 8) return ["Advanced", "C1"];

	return ["Proficiency", "C2"];
};

export const getGradingLabel = (score: number, grading: Step[]) => {
	for (const step of grading) {
		if (score >= step.min && score <= step.max) return step.label;
	}

	return "N/A";
};

export const averageLevelCalculator = (users: User[], studentStats: Stat[]) => {
	const formattedStats = studentStats
		.map((s) => ({
			focus: users.find((u) => u.id === s.user)?.focus,
			score: s.score,
			module: s.module,
		}))
		.filter((f) => !!f.focus);
	const bandScores = formattedStats.map((s) => ({
		module: s.module,
		level: calculateBandScore(s.score.correct, s.score.total, s.module, s.focus!),
	}));

	const levels: {[key in Module]: number} = {
		reading: 0,
		listening: 0,
		writing: 0,
		speaking: 0,
		level: 0,
	};
	bandScores.forEach((b) => (levels[b.module] += b.level));

	return calculateAverageLevel(levels);
};
