/**
 * @file Character sheet panel for viewing and editing the player's character.
 * @copyright nhcarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */
/* eslint-disable max-lines-per-function -- Complex component with many fields */
/* eslint-disable complexity -- Many conditional render paths for optional fields */
/* eslint-disable max-statements -- Component requires many state declarations */
/* eslint-disable max-lines -- Large component with editing and view modes */
import {
  DEFAULT_PROFILE_SETTINGS,
  STORY_CHAPTERS,
  type EquipmentBonus,
  type EquipmentRarity,
  type EquipmentType,
  type ProfileSettings,
} from "@elysium/types";
import { type ChangeEvent, type JSX, useEffect, useRef, useState } from "react";
import { updateProfile } from "../../api/client.js";
import { useGame } from "../../context/gameContext.js";
import { logError } from "../../utils/logError.js";

interface EquippedItem {
  name:   string;
  type:   EquipmentType;
  rarity: EquipmentRarity;
  bonus:  EquipmentBonus;
}

interface CharacterSheetData {
  characterName:    string;
  pronouns:         string;
  characterRace:    string;
  characterClass:   string;
  bio:              string;
  guildName:        string;
  guildDescription: string;
  activeTitle:      string;
  unlockedTitles:   Array<{ id: string; name: string }>;
  equippedItems:    Array<EquippedItem>;
}

const emptySheet: CharacterSheetData = {
  activeTitle:      "",
  bio:              "",
  characterClass:   "",
  characterName:    "",
  characterRace:    "",
  equippedItems:    [],
  guildDescription: "",
  guildName:        "",
  pronouns:         "",
  unlockedTitles:   [],
};

const slotIcons: Record<EquipmentType, string> = {
  armour:  "🛡️",
  trinket: "💍",
  weapon:  "⚔️",
};

/**
 * Formats an equipment bonus as a human-readable string.
 * @param bonus - The equipment bonus to format.
 * @returns The formatted bonus string.
 */
const formatBonus = (bonus: EquipmentBonus): string => {
  const parts: Array<string> = [];
  if (bonus.goldMultiplier !== undefined) {
    const pct = Math.round((bonus.goldMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Gold Income`);
  }
  if (bonus.combatMultiplier !== undefined) {
    const pct = Math.round((bonus.combatMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Combat Power`);
  }
  if (bonus.clickMultiplier !== undefined) {
    const pct = Math.round((bonus.clickMultiplier - 1) * 100);
    parts.push(`+${String(pct)}% Click Power`);
  }
  return parts.join(" · ");
};

/**
 * Renders the character sheet panel for viewing and editing player profile.
 * @returns The JSX element.
 */
const CharacterSheetPanel = (): JSX.Element => {
  const { state, loginStreak } = useGame();
  const player = state?.player;

  const [ sheet, setSheet ] = useState<CharacterSheetData>(emptySheet);
  const [ draft, setDraft ] = useState<CharacterSheetData>(emptySheet);
  const [ editing, setEditing ] = useState(false);
  const [ loading, setLoading ] = useState(true);
  const [ saving, setSaving ] = useState(false);
  const [ error, setError ] = useState<string | null>(null);
  const [ saved, setSaved ] = useState(false);
  const [ copied, setCopied ] = useState(false);
  const savedSettingsReference = useRef<ProfileSettings>({
    ...DEFAULT_PROFILE_SETTINGS,
  });

  useEffect(() => {
    if (player?.discordId === undefined || player.discordId === "") {
      return;
    }
    fetch(`/api/profile/${player.discordId}`).
      then(async(response) => {
        if (!response.ok) {
          return;
        }
        // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- API response cast
        const data = (await response.json()) as {
          characterName:    string;
          pronouns:         string;
          characterRace:    string;
          characterClass:   string;
          bio:              string;
          guildName:        string;
          guildDescription: string;
          profileSettings:  ProfileSettings;
          activeTitle:      string;
          unlockedTitles:   Array<{ id: string; name: string }>;
          equippedItems:    Array<EquippedItem>;
        };
        const loaded: CharacterSheetData = {
          activeTitle:      data.activeTitle,
          bio:              data.bio,
          characterClass:   data.characterClass,
          characterName:    data.characterName,
          characterRace:    data.characterRace,
          equippedItems:    data.equippedItems,
          guildDescription: data.guildDescription,
          guildName:        data.guildName,
          pronouns:         data.pronouns,
          unlockedTitles:   data.unlockedTitles,
        };
        setSheet(loaded);
        setDraft(loaded);
        savedSettingsReference.current = {
          ...DEFAULT_PROFILE_SETTINGS,
          ...data.profileSettings,
        };
      }).
      catch(() => {

        /* Fall back to empty */
      }).
      finally(() => {
        setLoading(false);
      });
  }, [ player?.discordId ]);

  function handleEdit(): void {
    setDraft({ ...sheet });
    setEditing(true);
    setError(null);
    setSaved(false);
  }

  function handleCancel(): void {
    setEditing(false);
    setError(null);
  }

  async function handleSave(): Promise<void> {
    setSaving(true);
    setError(null);
    try {
      const characterName
        = draft.characterName === ""
          ? player?.characterName ?? ""
          : draft.characterName;
      await updateProfile({
        activeTitle:      draft.activeTitle,
        bio:              draft.bio,
        characterClass:   draft.characterClass,
        characterName:    characterName,
        characterRace:    draft.characterRace,
        guildDescription: draft.guildDescription,
        guildName:        draft.guildName,
        profileSettings:  savedSettingsReference.current,
        pronouns:         draft.pronouns,
      });
      setSheet({ ...draft });
      setSaved(true);
      setTimeout(() => {
        setEditing(false);
        setSaved(false);
      }, 900);
    } catch (error_) {
      setError(error_ instanceof Error
        ? error_.message
        : "Failed to save");
    } finally {
      setSaving(false);
    }
  }

  function handleSaveClick(): void {
    void handleSave();
  }

  function handleShareClick(): void {
    const discordId = player?.discordId ?? "";
    const url = `${window.location.origin}/character/${discordId}`;
    void navigator.clipboard.writeText(url).
      then(() => {
        setCopied(true);
        setTimeout(() => {
          setCopied(false);
        }, 2000);
      }).
      catch((error_: unknown) => {
        logError("clipboard_copy", error_);
      });
  }

  function handleNameChange(event: ChangeEvent<HTMLInputElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, characterName: value };
    });
  }

  function handlePronounsChange(event: ChangeEvent<HTMLInputElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, pronouns: value };
    });
  }

  function handleRaceChange(event: ChangeEvent<HTMLInputElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, characterRace: value };
    });
  }

  function handleClassChange(event: ChangeEvent<HTMLInputElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, characterClass: value };
    });
  }

  function handleBioChange(event: ChangeEvent<HTMLTextAreaElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, bio: value };
    });
  }

  function handleTitleChange(event: ChangeEvent<HTMLSelectElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, activeTitle: value };
    });
  }

  function handleGuildNameChange(event: ChangeEvent<HTMLInputElement>): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, guildName: value };
    });
  }

  function handleGuildDescChange(
    event: ChangeEvent<HTMLTextAreaElement>,
  ): void {
    const { value } = event.target;
    setDraft((current) => {
      return { ...current, guildDescription: value };
    });
  }

  if (loading) {
    return (
      <section className="panel">
        <p>{"Loading character sheet…"}</p>
      </section>
    );
  }

  if (editing) {
    const isSaveDisabled = saving || draft.characterName.trim() === "";
    let saveLabel = "Save";
    if (saving) {
      saveLabel = "Saving…";
    }
    if (saved) {
      saveLabel = "✓ Saved!";
    }
    return (
      <section className="panel character-sheet-panel">
        <div className="panel-header">
          <h2>{"📋 Character Sheet"}</h2>
        </div>

        <div className="character-sheet-form">
          <div className="character-sheet-section">
            <h3 className="character-sheet-section-title">{"⚔️ Character"}</h3>

            <label className="character-sheet-label" htmlFor="cs-name">
              {"Character Name"}
            </label>
            <input
              className="character-sheet-input"
              id="cs-name"
              maxLength={32}
              onChange={handleNameChange}
              placeholder="Your character's name"
              type="text"
              value={draft.characterName}
            />
            <span className="character-sheet-hint">
              {draft.characterName.length}
              {" / 32"}
            </span>

            <label className="character-sheet-label" htmlFor="cs-pronouns">
              {"Pronouns"}
            </label>
            <input
              className="character-sheet-input"
              id="cs-pronouns"
              maxLength={20}
              onChange={handlePronounsChange}
              placeholder="e.g. she/her, he/him, they/them"
              type="text"
              value={draft.pronouns}
            />
            <span className="character-sheet-hint">
              {draft.pronouns.length}
              {" / 20"}
            </span>

            <label className="character-sheet-label" htmlFor="cs-race">
              {"Race"}
            </label>
            <input
              className="character-sheet-input"
              id="cs-race"
              maxLength={32}
              onChange={handleRaceChange}
              placeholder="e.g. Elf, Dwarf, Human, Tiefling…"
              type="text"
              value={draft.characterRace}
            />
            <span className="character-sheet-hint">
              {draft.characterRace.length}
              {" / 32"}
            </span>

            <label className="character-sheet-label" htmlFor="cs-class">
              {"Class"}
            </label>
            <input
              className="character-sheet-input"
              id="cs-class"
              maxLength={32}
              onChange={handleClassChange}
              placeholder="e.g. Paladin, Archmage, Shadow Rogue…"
              type="text"
              value={draft.characterClass}
            />
            <span className="character-sheet-hint">
              {draft.characterClass.length}
              {" / 32"}
            </span>

            <label className="character-sheet-label" htmlFor="cs-bio">
              {"About Your Character"}
            </label>
            <textarea
              className="character-sheet-textarea"
              id="cs-bio"
              maxLength={200}
              onChange={handleBioChange}
              placeholder={
                "Describe your character's story, personality, or appearance…"
              }
              rows={4}
              value={draft.bio}
            />
            <span className="character-sheet-hint">
              {draft.bio.length}
              {" / 200"}
            </span>

            {draft.unlockedTitles.length > 0
              && <>
                <label className="character-sheet-label" htmlFor="cs-title">
                  {"Active Title"}
                </label>
                <select
                  className="character-sheet-input"
                  id="cs-title"
                  onChange={handleTitleChange}
                  value={draft.activeTitle}
                >
                  <option value="">{"— None —"}</option>
                  {draft.unlockedTitles.map((title) => {
                    return (
                      <option key={title.id} value={title.id}>
                        {title.name}
                      </option>
                    );
                  })}
                </select>
              </>
            }
          </div>

          <div className="character-sheet-section">
            <h3 className="character-sheet-section-title">{"🏰 Guild"}</h3>

            <label className="character-sheet-label" htmlFor="cs-guild-name">
              {"Guild Name"}
            </label>
            <input
              className="character-sheet-input"
              id="cs-guild-name"
              maxLength={64}
              onChange={handleGuildNameChange}
              placeholder="Name your guild"
              type="text"
              value={draft.guildName}
            />
            <span className="character-sheet-hint">
              {draft.guildName.length}
              {" / 64"}
            </span>

            <label className="character-sheet-label" htmlFor="cs-guild-desc">
              {"Guild Description"}
            </label>
            <textarea
              className="character-sheet-textarea"
              id="cs-guild-desc"
              maxLength={500}
              onChange={handleGuildDescChange}
              placeholder="Describe your guild's history, goals, or lore…"
              rows={6}
              value={draft.guildDescription}
            />
            <span className="character-sheet-hint">
              {draft.guildDescription.length}
              {" / 500"}
            </span>
          </div>

          {error === null
            ? null
            : <p className="character-sheet-error">{error}</p>
          }

          <div className="character-sheet-actions">
            <button
              className="character-sheet-cancel"
              onClick={handleCancel}
              type="button"
            >
              {"Cancel"}
            </button>
            <button
              className="character-sheet-save"
              disabled={isSaveDisabled}
              onClick={handleSaveClick}
              type="button"
            >
              {saveLabel}
            </button>
          </div>
        </div>
      </section>
    );
  }

  const subtitleParts = [ sheet.characterRace, sheet.characterClass ].filter(
    (part) => {
      return part !== "";
    },
  );
  const subtitle = subtitleParts.join(" · ");

  const completedChapters = state?.story?.completedChapters ?? [];

  return (
    <section className="panel character-sheet-panel">
      <div className="panel-header">
        <h2>{"📋 Character Sheet"}</h2>
        <div className="character-sheet-header-actions">
          <button
            className="character-sheet-edit-btn"
            onClick={handleShareClick}
            type="button"
          >
            {copied
              ? "✓ Copied!"
              : "🔗 Share"}
          </button>
          <a className="character-sheet-edit-btn" href="/leaderboards">
            {"🏆 Boards"}
          </a>
          <button
            className="character-sheet-edit-btn"
            onClick={handleEdit}
            type="button"
          >
            {"✏️ Edit"}
          </button>
        </div>
      </div>

      <div className="character-sheet-view">
        <div className="character-sheet-section">
          <h3 className="character-sheet-section-title">{"⚔️ Character"}</h3>
          <div className="character-sheet-field">
            <span className="character-sheet-field-label">{"Name"}</span>
            <span className="character-sheet-field-value">
              {sheet.characterName === ""
                ? <em className="character-sheet-empty">{"Not set"}</em>
                : sheet.characterName
              }
            </span>
          </div>
          <div className="character-sheet-field">
            <span className="character-sheet-field-label">{"Streak"}</span>
            <span className="character-sheet-streak">
              {"🔥 "}
              {loginStreak}
              {"-day login streak"}
            </span>
          </div>
          {sheet.activeTitle === ""
            ? null
            : <div className="character-sheet-field">
              <span className="character-sheet-field-label">{"Title"}</span>
              <span
                className={"character-sheet-field-value character-sheet-title"}
              >
                {sheet.unlockedTitles.find((title) => {
                  return title.id === sheet.activeTitle;
                })?.name ?? sheet.activeTitle}
              </span>
            </div>
          }
          {sheet.pronouns === ""
            ? null
            : <div className="character-sheet-field">
              <span className="character-sheet-field-label">{"Pronouns"}</span>
              <span className="character-sheet-field-value">
                {sheet.pronouns}
              </span>
            </div>
          }
          {subtitle === ""
            ? null
            : <div className="character-sheet-field">
              <span className="character-sheet-field-label">{"Identity"}</span>
              <span className="character-sheet-field-value">{subtitle}</span>
            </div>
          }
          {sheet.bio === ""
            ? null
            : <div className="character-sheet-bio">
              <span className="character-sheet-field-label">{"About"}</span>
              <p className="character-sheet-bio-text">{sheet.bio}</p>
            </div>
          }
        </div>

        <div className="character-sheet-section">
          <h3 className="character-sheet-section-title">{"🗡️ Equipment"}</h3>
          {sheet.equippedItems.length > 0
            ? <div className="character-sheet-equipment-list">
              {sheet.equippedItems.map((item) => {
                return (
                  <div
                    className="character-sheet-equipment-item"
                    key={item.type}
                  >
                    <div className="character-sheet-equipment-header">
                      <span className="character-sheet-equipment-slot">
                        {slotIcons[item.type]}
                      </span>
                      <span
                        className={
                          "character-sheet-equipment-name"
                          + ` character-sheet-rarity--${item.rarity}`
                        }
                      >
                        {item.name}
                      </span>
                      <span
                        className={
                          "character-sheet-equipment-rarity"
                          + ` character-sheet-rarity--${item.rarity}`
                        }
                      >
                        {item.rarity}
                      </span>
                    </div>
                    <p className="character-sheet-equipment-bonus">
                      {formatBonus(item.bonus)}
                    </p>
                  </div>
                );
              })}
            </div>
            : <p className="character-sheet-empty">
              {"No equipment found. Defeat bosses to earn gear!"}
            </p>
          }
        </div>

        <div className="character-sheet-section">
          <h3 className="character-sheet-section-title">{"🏰 Guild"}</h3>
          {sheet.guildName === ""
            ? <p className="character-sheet-empty">
              {"No guild registered yet. Click ✏️ Edit to add one!"}
            </p>
            : <>
              <div className="character-sheet-field">
                <span className="character-sheet-field-label">{"Name"}</span>
                <span className="character-sheet-field-value">
                  {sheet.guildName}
                </span>
              </div>
              {sheet.guildDescription === ""
                ? null
                : <div className="character-sheet-bio">
                  <span className="character-sheet-field-label">{"Lore"}</span>
                  <p className="character-sheet-bio-text">
                    {sheet.guildDescription}
                  </p>
                </div>
              }
            </>
          }
        </div>

        {completedChapters.length === 0
          ? null
          : <div className="character-sheet-section">
            <h3 className="character-sheet-section-title">
              {"📖 Story Choices"}
            </h3>
            {completedChapters.map((completion) => {
              const chapter = STORY_CHAPTERS.find((candidate) => {
                return candidate.id === completion.chapterId;
              });
              if (chapter === undefined) {
                return null;
              }
              const choice = chapter.choices.find((candidate) => {
                return candidate.id === completion.choiceId;
              });
              if (choice === undefined) {
                return null;
              }
              const characterName
                = player?.characterName === ""
                || player?.characterName === undefined
                  ? "the guild leader"
                  : player.characterName;
              const outcome = choice.outcome.replaceAll(
                "{characterName}",
                characterName,
              );
              return (
                <div
                  className="character-sheet-story-entry"
                  key={completion.chapterId}
                >
                  <span className="character-sheet-story-chapter">
                    {chapter.title}
                  </span>
                  <span className="character-sheet-story-choice">
                    {choice.label}
                  </span>
                  <p className="character-sheet-story-outcome">{outcome}</p>
                </div>
              );
            })}
          </div>
        }
      </div>
    </section>
  );
};

export { CharacterSheetPanel };
