import {
	rate as openskillRate,
	ordinal,
	type Rating,
	rating,
	type Team,
} from "openskill";
import invariant from "~/utils/invariant";
import type { TierName } from "./mmr-constants";
import { TIERS } from "./mmr-constants";

const TAU = 0.3;

export function ordinalToSp(ordinal: number) {
	return toTwoDecimals(ordinal * 15 + 1000);
}

export function ordinalToRoundedSp(ordinal: number) {
	return Math.round(ordinalToSp(ordinal));
}

function toTwoDecimals(value: number) {
	return Number(value.toFixed(2));
}

export function rate(teams: Team[], secondaryTeams?: [[Rating], [Rating]]) {
	if (secondaryTeams) return rateConservative(teams, secondaryTeams);

	return openskillRate(teams, { tau: TAU, limitSigma: true });
}

// when ranking teams we rate the team against the actual team rating that it played against
// as well as against the average ratings of the players on the team
// then they get the bigger boost of the two (if won) or the smaller penalty of the two (if lost)
// this is to avoid situations where teams might unexpectedly lose a huge amount of points
// due to other team score not being accurate (not enough games played) to their perceived skill level
function rateConservative(
	teams: Team[],
	secondaryTeams: [[Rating], [Rating]],
): [[Rating], [Rating]] {
	const [[ordinaryRatingForWinner], [ordinaryRatingForLoser]] = openskillRate(
		teams,
		{
			tau: TAU,
			limitSigma: true,
		},
	);

	const [, [conservativeRatingForLoser]] = openskillRate(
		[secondaryTeams[0], teams[1]],
		{
			tau: TAU,
			limitSigma: true,
		},
	);

	const [[conservativeRatingForWinner]] = openskillRate(
		[teams[0], secondaryTeams[1]],
		{
			tau: TAU,
			limitSigma: true,
		},
	);

	const winnerRating =
		ordinal(ordinaryRatingForWinner) > ordinal(conservativeRatingForWinner)
			? ordinaryRatingForWinner
			: conservativeRatingForWinner;

	const loserRating =
		ordinal(ordinaryRatingForLoser) > ordinal(conservativeRatingForLoser)
			? ordinaryRatingForLoser
			: conservativeRatingForLoser;

	return [[winnerRating], [loserRating]];
}

export function userIdsToIdentifier(userIds: number[]) {
	invariant(userIds.length === 4, "userIds for identifier must be length 4");
	return [...userIds].sort((a, b) => a - b).join("-");
}

export function identifierToUserIds(identifier: string) {
	return identifier.split("-").map(Number);
}

export function defaultOrdinal() {
	return ordinal(rating());
}

export function compareTwoTiers(tier1: TierName, tier2: TierName) {
	return (
		TIERS.findIndex(({ name }) => name === tier1) -
		TIERS.findIndex(({ name }) => name === tier2)
	);
}
