type Difficulty = {
  name: DifficultyName;
  gamemode?: string;
  color: string;
};

type DifficultyName = "Easy" | "Normal" | "Hard" | "Expert" | "Expert+";

const difficulties: Difficulty[] = [
  { name: "Easy", color: "#3cb371" },
  { name: "Normal", color: "#59b0f4" },
  { name: "Hard", color: "#FF6347" },
  { name: "Expert", color: "#bf2a42" },
  { name: "Expert+", color: "#8f48db" },
];

export type ScoreBadge = {
  name: string;
  min: number | null;
  max: number | null;
  color: string;
};

const scoreBadges: ScoreBadge[] = [
  { name: "SS+", min: 95, max: null, color: getDifficulty("Expert+")!.color },
  { name: "SS", min: 90, max: 95, color: getDifficulty("Expert")!.color },
  { name: "S+", min: 85, max: 90, color: getDifficulty("Hard")!.color },
  { name: "S", min: 80, max: 85, color: getDifficulty("Normal")!.color },
  { name: "A", min: 70, max: 80, color: getDifficulty("Easy")!.color },
  { name: "-", min: null, max: 70, color: "hsl(var(--accent))" },
];

/**
 * Returns the color based on the accuracy provided.
 *
 * @param acc - The accuracy for the score
 * @returns The corresponding color for the accuracy.
 */
export function getScoreBadgeFromAccuracy(acc: number): ScoreBadge {
  // Check for SS+ first since it has no upper limit
  if (acc >= 95) {
    return scoreBadges[0]; // SS+ color
  }

  // Iterate through the rest of the badges
  for (const badge of scoreBadges) {
    const min = badge.min ?? -Infinity; // Treat null `min` as -Infinity
    const max = badge.max ?? Infinity; // Treat null `max` as Infinity

    // Check if the accuracy falls within the badge's range
    if (acc >= min && acc < (max === null ? Infinity : max)) {
      return badge; // Return the color of the matching badge
    }
  }

  // Fallback color if no badge matches (should not happen)
  return scoreBadges[scoreBadges.length - 1];
}

/**
 * Parses a raw difficulty into a {@link Difficulty}
 * Example: _Easy_SoloStandard -> { name: "Easy", type: "Standard", color: "#59b0f4" }
 *
 * @param rawDifficulty the raw difficulty to parse
 * @return the parsed difficulty
 */
export function getDifficultyFromRawDifficulty(rawDifficulty: string): Difficulty {
  const [name, ...type] = rawDifficulty
    .replace("Plus", "+") // Replaces Plus with + so we can match it to our difficulty names
    .replace("Solo", "") // Removes "Solo"
    .replace(/^_+|_+$/g, "") // Removes leading and trailing underscores
    .split("_");
  const difficulty = difficulties.find(d => d.name === name);
  if (!difficulty) {
    throw new Error(`Unknown difficulty: ${rawDifficulty}`);
  }
  return {
    ...difficulty,
    gamemode: type.join("_"),
  };
}

/**
 * Gets a {@link Difficulty} from its name
 *
 * @param diff the name of the difficulty
 * @returns the difficulty
 */
export function getDifficulty(diff: DifficultyName) {
  return difficulties.find(d => d.name === diff);
}

/**
 * Turns the difficulty of a song into a color
 *
 * @param diff the difficulty to get the color for
 * @returns the color for the difficulty
 */
export function songDifficultyToColor(diff: string) {
  return getDifficultyFromRawDifficulty(diff).color;
}
