/**
 * @file Achievement panel component displaying all game achievements.
 * @copyright nhcarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */
/* eslint-disable react/no-multi-comp -- Sub-component is tightly coupled to this panel */
import { type JSX, useState } from "react";
import { useGame } from "../../context/gameContext.js";
import { cdnImage } from "../../utils/cdn.js";
import { LockToggle } from "../ui/lockToggle.js";
import type { Achievement, GameState } from "@elysium/types";

/**
 * Returns the plural form of a word based on a count.
 * @param count - The count to check.
 * @param word - The base word to pluralise.
 * @returns The pluralised word string.
 */
const pluralise = (count: number, word: string): string => {
  return count > 1
    ? `${word}s`
    : word;
};

/**
 * Generates a human-readable condition description for an achievement.
 * @param achievement - The achievement to describe.
 * @param formatNumber - The number formatting utility function.
 * @returns A string describing the achievement condition.
 */
const conditionDescription = (
  achievement: Achievement,
  formatNumber: (n: number)=> string,
): string => {
  const { condition } = achievement;
  switch (condition.type) {
    case "totalGoldEarned":
      return `Earn ${formatNumber(condition.amount)} total gold`;
    case "totalClicks":
      return `Click ${formatNumber(condition.amount)} times`;
    case "bossesDefeated":
      return `Defeat ${String(condition.amount)} ${pluralise(condition.amount, "boss")}`;
    case "questsCompleted":
      return `Complete ${String(condition.amount)} ${pluralise(condition.amount, "quest")}`;
    case "adventurerTotal":
      return `Recruit ${formatNumber(condition.amount)} total adventurers`;
    case "prestigeCount":
      return `Prestige ${String(condition.amount)} ${pluralise(condition.amount, "time")}`;
    case "equipmentOwned":
      return `Own ${String(condition.amount)} equipment ${pluralise(condition.amount, "item")}`;
    default:
      return "Unknown condition";
  }
};

/**
 * Returns the player's current progress value toward an achievement's unlock condition,
 * mirroring the logic used by the tick engine's checkAchievements function.
 * @param achievement - The achievement to evaluate progress for.
 * @param state - The current game state.
 * @returns The current numeric progress toward the achievement condition.
 */
const getCurrentProgress = (
  achievement: Achievement,
  state: GameState,
): number => {
  const { condition } = achievement;
  switch (condition.type) {
    case "totalGoldEarned":
      return state.player.totalGoldEarned;
    case "totalClicks":
      return state.player.totalClicks;
    case "bossesDefeated":
      return state.bosses.filter((boss) => {
        return boss.status === "defeated";
      }).length;
    case "questsCompleted":
      return state.quests.filter((quest) => {
        return quest.status === "completed";
      }).length;
    case "adventurerTotal":
      return state.adventurers.reduce((sum, adventurer) => {
        return sum + adventurer.count;
      }, 0);
    case "prestigeCount":
      return state.prestige.count;
    case "equipmentOwned":
      return state.equipment.filter((item) => {
        return item.owned;
      }).length;
    default:
      return 0;
  }
};

interface AchievementCardProperties {
  readonly achievement:   Achievement;
  readonly formatNumber:  (n: number)=> string;
  readonly progressValue: number;
}

/**
 * Renders a single achievement card.
 * @param props - The achievement card properties.
 * @param props.achievement - The achievement to display.
 * @param props.formatNumber - The number formatting utility function.
 * @param props.progressValue - The player's current progress toward the unlock condition.
 * @returns The JSX element.
 */
// eslint-disable-next-line max-lines-per-function -- Progress bar adds necessary lines for locked state
const AchievementCard = ({
  achievement,
  formatNumber,
  progressValue,
}: AchievementCardProperties): JSX.Element => {
  const isUnlocked = achievement.unlockedAt !== null;
  const crystals = achievement.reward?.crystals;
  const cappedProgress = Math.min(progressValue, achievement.condition.amount);

  return (
    <div className={`achievement-card ${isUnlocked
      ? "unlocked"
      : "locked"}`}>
      <img
        alt={achievement.name}
        className="card-thumbnail"
        src={cdnImage("achievements", achievement.id)}
      />
      <div className="achievement-info">
        <h3>{achievement.name}</h3>
        <p>{achievement.description}</p>
        <p className="achievement-condition">
          {conditionDescription(achievement, formatNumber)}
        </p>
        {!isUnlocked
          && <div className="achievement-progress">
            <progress
              max={achievement.condition.amount}
              value={cappedProgress}
            />
            <span className="achievement-progress-label">
              {formatNumber(progressValue)}
              {" / "}
              {formatNumber(achievement.condition.amount)}
            </span>
          </div>
        }
        {crystals !== undefined
          && <p className="achievement-reward">
            {"💎 +"}
            {crystals}
            {" Crystals"}
          </p>
        }
      </div>
      <div className="achievement-status">
        {isUnlocked
          ? <span className="achievement-unlocked-badge">{"✓ Unlocked"}</span>
          : <span className="achievement-locked-badge">{"🔒"}</span>
        }
      </div>
    </div>
  );
};

/**
 * Renders the achievement panel with all achievements.
 * @returns The JSX element.
 */
// eslint-disable-next-line max-lines-per-function -- Achievement panel renders many achievement states
const AchievementPanel = (): JSX.Element => {
  const { state, formatNumber } = useGame();
  const [ showLocked, setShowLocked ] = useState(true);

  if (state === null) {
    return (
      <section className="panel">
        <p>{"Loading..."}</p>
      </section>
    );
  }

  const achievementList = state.achievements;
  const unlocked = achievementList.filter((a) => {
    return a.unlockedAt !== null;
  });
  const locked = achievementList.filter((a) => {
    return a.unlockedAt === null;
  });
  const visible = showLocked
    ? achievementList
    : unlocked;

  function handleToggle(): void {
    setShowLocked((current) => {
      return !current;
    });
  }

  return (
    <section className="panel achievement-panel">
      <div className="panel-header">
        <h2>{"Achievements"}</h2>
        <LockToggle
          lockedCount={locked.length}
          onToggle={handleToggle}
          showLocked={showLocked}
        />
      </div>
      <p className="achievement-progress">
        {unlocked.length}
        {" / "}
        {achievementList.length}
        {" unlocked"}
      </p>
      <div className="achievement-list">
        {visible.map((achievement) => {
          return (
            <AchievementCard
              achievement={achievement}
              formatNumber={formatNumber}
              key={achievement.id}
              progressValue={getCurrentProgress(achievement, state)}
            />
          );
        })}
      </div>
    </section>
  );
};

export { AchievementPanel };
