/**
 * @file Equipment panel component for managing owned and available equipment.
 * @copyright nhcarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */
/* eslint-disable react/no-multi-comp -- Sub-component is tightly coupled to the panel */
/* eslint-disable max-lines-per-function -- Complex component with many render paths */
/* eslint-disable complexity -- Complex component with many conditional render paths */
/* eslint-disable max-lines -- Equipment panel with set bonus display and sort logic */
import { type JSX, useState } from "react";
import { useGame } from "../../context/gameContext.js";
import { EQUIPMENT_SETS } from "../../data/equipmentSets.js";
import { cdnImage } from "../../utils/cdn.js";
import { LockToggle } from "../ui/lockToggle.js";
import type { Equipment, EquipmentType } from "@elysium/types";

const rarityLabel: Record<string, string> = {
  common:    "Common",
  epic:      "Epic",
  legendary: "Legendary",
  rare:      "Rare",
};

/**
 * Computes a human-readable bonus description for a piece of equipment.
 * @param item - The equipment item.
 * @returns The formatted bonus description.
 */
const bonusDescription = (item: Equipment): string => {
  const parts: Array<string> = [];
  if (item.bonus.combatMultiplier !== undefined) {
    const pct = Math.round((item.bonus.combatMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Boss Combat`);
  }
  if (item.bonus.goldMultiplier !== undefined) {
    const pct = Math.round((item.bonus.goldMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Gold/s`);
  }
  if (item.bonus.clickMultiplier !== undefined) {
    const pct = Math.round((item.bonus.clickMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Click`);
  }
  return parts.join(", ");
};

/**
 * Formats an equipment cost as a readable string.
 * @param cost - The cost object with gold, essence, and crystals.
 * @param cost.gold - The gold component of the cost.
 * @param cost.essence - The essence component of the cost.
 * @param cost.crystals - The crystals component of the cost.
 * @returns The formatted cost string.
 */
const costLabel = (cost: {
  gold:     number;
  essence:  number;
  crystals: number;
}): string => {
  const parts: Array<string> = [];
  if (cost.gold > 0) {
    parts.push(`🪙 ${cost.gold.toLocaleString()}`);
  }
  if (cost.essence > 0) {
    parts.push(`✨ ${cost.essence.toLocaleString()}`);
  }
  if (cost.crystals > 0) {
    parts.push(`💎 ${cost.crystals.toLocaleString()}`);
  }
  return parts.join("  ");
};

interface EquipmentCardProperties {
  readonly item:         Equipment;
  readonly gold:         number;
  readonly essence:      number;
  readonly crystals:     number;
  readonly dropBossName: string | undefined;
  readonly setName:      string | undefined;
}

/**
 * Renders a single equipment card.
 * @param props - The equipment card properties.
 * @param props.item - The equipment item data.
 * @param props.gold - The current gold amount.
 * @param props.essence - The current essence amount.
 * @param props.crystals - The current crystals amount.
 * @param props.dropBossName - The name of the boss that drops this item.
 * @param props.setName - The name of the set this item belongs to.
 * @returns The JSX element.
 */
const EquipmentCard = ({
  item,
  gold,
  essence,
  crystals,
  dropBossName,
  setName,
}: EquipmentCardProperties): JSX.Element => {
  const { equipItem, buyEquipment } = useGame();

  const canAfford
    = item.cost !== undefined
    && gold >= item.cost.gold
    && essence >= item.cost.essence
    && crystals >= item.cost.crystals;

  function handleBuy(): void {
    buyEquipment(item.id);
  }
  function handleEquip(): void {
    equipItem(item.id);
  }

  const ownedClass = item.owned
    ? ""
    : "not-owned";
  const equippedClass = item.equipped
    ? "equipped"
    : "";

  return (
    <div
      className={`equipment-card rarity-${item.rarity} ${equippedClass} ${ownedClass}`}
    >
      <img
        alt={item.name}
        className="card-thumbnail"
        src={cdnImage("equipment", item.id)}
      />
      <div className="equipment-info">
        <div className="equipment-name-row">
          <h3>{item.name}</h3>
          <span className={`rarity-badge rarity-${item.rarity}`}>
            {rarityLabel[item.rarity]}
          </span>
        </div>
        <p className="equipment-description">{item.description}</p>
        <p className="equipment-bonus">{bonusDescription(item)}</p>
        {setName === undefined
          ? null
          : <span className="equipment-set-badge">
            {"🔗 "}
            {setName}
          </span>
        }
        {item.owned || item.cost === undefined
          ? null
          : <p className="equipment-cost">{costLabel(item.cost)}</p>
        }
      </div>
      <div className="equipment-action">
        {!item.owned && item.cost === undefined
          && <span className="equipment-locked">
            {dropBossName === undefined
              ? "🔒 Boss drop"
              : `⚔️ Drop: ${dropBossName}`}
          </span>
        }
        {item.owned || item.cost === undefined
          ? null
          : <button
            className="equip-button"
            disabled={!canAfford}
            onClick={handleBuy}
            type="button"
          >
            {canAfford
              ? "Purchase"
              : "Can't afford"}
          </button>
        }
        {item.owned && item.equipped
          ? <span className="equipment-equipped-badge">{"✓ Equipped"}</span>
          : null}
        {item.owned && !item.equipped
          ? <button
            className="equip-button"
            onClick={handleEquip}
            type="button"
          >
            {"Equip"}
          </button>

          : null}
      </div>
    </div>
  );
};

/**
 * Computes a combined power score for sorting — sum of all bonus multipliers.
 * Using the sum (rather than a single stat) keeps hybrid items in sensible order.
 * @param item - The equipment piece whose bonus multipliers are summed.
 * @returns The combined bonus value.
 */
const equipmentPower = (item: Equipment): number => {
  return (
    (item.bonus.combatMultiplier ?? 1)
    + (item.bonus.goldMultiplier ?? 1)
    + (item.bonus.clickMultiplier ?? 1)
  );
};

const slotOrder: Array<EquipmentType> = [ "weapon", "armour", "trinket" ];
const slotLabel: Record<EquipmentType, string> = {
  armour:  "🛡️ Armour",
  trinket: "💍 Trinkets",
  weapon:  "⚔️ Weapons",
};

/**
 * Renders the equipment panel with all owned and available equipment.
 * @returns The JSX element.
 */
const EquipmentPanel = (): JSX.Element => {
  const { state } = useGame();
  const [ showLocked, setShowLocked ] = useState(true);

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

  const { bosses, equipment, resources } = state;
  const unownedCount = equipment.filter((item) => {
    return !item.owned;
  }).length;

  const equipmentDropSources = new Map<string, string>();
  for (const { equipmentRewards, name: bossName } of bosses) {
    for (const equipmentId of equipmentRewards) {
      equipmentDropSources.set(equipmentId, bossName);
    }
  }

  const setNameById = new Map<string, string>(
    EQUIPMENT_SETS.map((equipSet) => {
      return [ equipSet.id, equipSet.name ];
    }),
  );

  const equippedItemIds = new Set(
    equipment.
      filter((item) => {
        return item.equipped;
      }).
      map((item) => {
        return item.id;
      }),
  );
  const activeSets = EQUIPMENT_SETS.map((set) => {
    const count = set.pieces.filter((id) => {
      return equippedItemIds.has(id);
    }).length;
    return { count, set };
  }).filter(({ count }) => {
    return count >= 2;
  });

  function setBonusDescription(
    equipSet: (typeof EQUIPMENT_SETS)[number],
    count: number,
  ): string {
    const parts: Array<string> = [];
    for (const threshold of [ 2, 3 ] as const) {
      if (count >= threshold) {
        const bonus = equipSet.bonuses[threshold];
        if (bonus.goldMultiplier !== undefined) {
          const pct = Math.round((bonus.goldMultiplier - 1) * 100);
          parts.push(`+${String(pct)}% Gold/s (${String(threshold)}pc)`);
        }
        if (bonus.combatMultiplier !== undefined) {
          const pct = Math.round((bonus.combatMultiplier - 1) * 100);
          parts.push(`+${String(pct)}% Boss Combat (${String(threshold)}pc)`);
        }
        if (bonus.clickMultiplier !== undefined) {
          const pct = Math.round((bonus.clickMultiplier - 1) * 100);
          parts.push(`+${String(pct)}% Click (${String(threshold)}pc)`);
        }
      }
    }
    return parts.join(", ");
  }

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

  return (
    <section className="panel equipment-panel">
      <div className="panel-header">
        <h2>{"Equipment"}</h2>
        <LockToggle
          lockedCount={unownedCount}
          onToggle={handleToggle}
          showLocked={showLocked}
        />
      </div>
      <p className="equipment-intro">
        {"Equipment drops from bosses and grants passive bonuses."
          + " Only one item per slot can be equipped at a time."
          + " Equip matching set pieces for bonus effects!"}
      </p>

      {activeSets.length > 0
        && <div className="active-sets">
          <h3 className="active-sets-heading">{"✨ Active Set Bonuses"}</h3>
          {activeSets.map(({ set, count }) => {
            return (
              <div className="active-set-row" key={set.id}>
                <span className="active-set-name">
                  {set.name}
                  {" ("}
                  {count}
                  {"/"}
                  {set.pieces.length}
                  {")"}
                </span>
                <span className="active-set-bonus">
                  {setBonusDescription(set, count)}
                </span>
              </div>
            );
          })}
        </div>
      }

      {slotOrder.map((slotType) => {
        const items = equipment.filter((item) => {
          return item.type === slotType && (showLocked || item.owned);
        }).sort((a, b) => {
          return equipmentPower(a) - equipmentPower(b);
        });
        return (
          <div className="equipment-slot-section" key={slotType}>
            <h3 className="slot-heading">{slotLabel[slotType]}</h3>
            <div className="equipment-list">
              {items.map((item) => {
                return (
                  <EquipmentCard
                    crystals={resources.crystals}
                    dropBossName={equipmentDropSources.get(item.id)}
                    essence={resources.essence}
                    gold={resources.gold}
                    item={item}
                    key={item.id}
                    setName={
                      item.setId === undefined
                        ? undefined
                        : setNameById.get(item.setId)
                    }
                  />
                );
              })}
              {items.length === 0
                && <p className="empty-zone">
                  {"No items to show in this slot."}
                </p>
              }
            </div>
          </div>
        );
      })}
    </section>
  );
};

export { EquipmentPanel };
