Source: ui.js

/**
 * @file ui.js
 * @description Handles all DOM interactions for the Pokémon Team Builder:
 * - Loads supported generations
 * - Loads generation-specific data
 * - Populates generation-dependent form fields
 * - Loads results from selected filters
 * - Adds/removes Pokémon to/from party
 * - Updates party display and weakness analysis
 */

import { fetchGenerations, fetchDexEntries, ViewPokemon } from './fetch.js';
import {
  results,
  selectedGeneration,
  selectedSprites,
  selectedVersion,
  generationOptions,
  generationTitle,
  generationDescription,
  generationInfo,
  loadingStatus,
  interactionStatus
} from './state.js';
import { party, updatePartyDisplay, clearParty } from './party.js';
import { calculateTeamWeaknesses } from './team.js';

let currentGenerationData = null;

/**
 * Loads supported Pokémon generations and adds them
 * to the generation dropdown.
 */
async function loadGenerations() {
  try {
    const generationData = await fetchGenerations();

    for (const generation of generationData.generations) {
      const option = document.createElement('option');

      option.value = generation.file;
      option.textContent = generation.name;

      selectedGeneration.appendChild(option);
    }
  } catch (error) {
    loadingStatus.textContent =
      `Unable to load available generations. ${error.message}`;

    loadingStatus.hidden = false;
    selectedGeneration.disabled = true;
  }
}


/**
 * Populates the sprite dropdown using the currently
 * selected generation's JSON data.
 *
 * @param {Object} generationData
 */
function populateSpriteOptions(generationData) {
  selectedSprites.innerHTML = `
    <option selected disabled value="">
      Select sprite variation
    </option>
  `;

  for (const sprite of generationData.spriteSets) {
    const option = document.createElement('option');

    option.value = sprite.id;
    option.textContent = sprite.name;

    selectedSprites.appendChild(option);
  }
}


/**
 * Populates the game version dropdown using the currently
 * selected generation's JSON data.
 *
 * @param {Object} generationData
 */
function populateVersionOptions(generationData) {
  selectedVersion.innerHTML = `
    <option selected disabled value="">
      Select game version
    </option>
  `;

  for (const version of generationData.versions) {
    const option = document.createElement('option');

    option.value = version.id;
    option.textContent = version.name;

    selectedVersion.appendChild(option);
  }
}


/**
 * Loads generation-specific data when the user selects
 * a generation.
 */
selectedGeneration.addEventListener('change', async function () {
  loadingStatus.textContent = 'Loading generation data...';
  loadingStatus.hidden = false;

  generationOptions.disabled = true;

  try {
    currentGenerationData =
      await fetchDexEntries(selectedGeneration.value);

    populateSpriteOptions(currentGenerationData);
    populateVersionOptions(currentGenerationData);

    generationTitle.textContent =
      currentGenerationData.displayName;

    generationDescription.textContent =
      currentGenerationData.description;

    generationInfo.hidden = false;
    generationOptions.disabled = false;

    document
      .getElementById('generationOptionsHelp')
      .hidden = true;

    results.innerHTML = '';
    clearParty(currentGenerationData);
  } catch (error) {
    currentGenerationData = null;

    generationInfo.hidden = true;
    results.innerHTML = '';

    document
      .getElementById('generationOptionsHelp')
      .hidden = false;

    loadingStatus.textContent =
      `Unable to load generation data. ${error.message}`;

    return;
  }

  loadingStatus.hidden = true;
});


/**
 * Clears the current party when the Clear Party button is activated.
 * Refreshes the party display and team analysis.
 */
document
  .getElementById('clearParty')
  .addEventListener('click', () => {
    clearParty(currentGenerationData);
    interactionStatus.textContent = 'Party cleared.';
    calculateTeamWeaknesses(currentGenerationData);
  });


/**
 * Loads available Pokémon when the Team Form is submitted.
 * Prevents page reload and shows filtered Pokémon based
 * on selected options.
 */
document
  .getElementById('TeamForm')
  .addEventListener('submit', function (evt) {
    evt.preventDefault();

    if (!currentGenerationData) {
      return;
    }

    results.innerHTML = '';
    interactionStatus.textContent = '';

    ViewPokemon(currentGenerationData);

    requestAnimationFrame(() => {
      interactionStatus.textContent = 'Available Pokémon loaded.';
    });
  });


/**
 * Adds or removes a Pokémon from the party when its result button
 * is activated.
 * Enforces a maximum party size of 6 Pokémon.
 */
results.addEventListener('click', function (e) {
  const button = e.target.closest('[data-pokdex]');

  if (!button) return;

  const dexId = button.dataset.pokdex;
  const name = button.dataset.name;

  if (party.length < 6 && !party.includes(dexId)) {
    party.push(dexId);

    button.setAttribute('aria-pressed', 'true');
    button.setAttribute('aria-label', `Remove ${name} from party`);
    button.title = `Remove ${name} from party`;

    interactionStatus.textContent =
      `${name} added to party. ${party.length} of 6 Pokémon selected.`;
  } else if (party.includes(dexId)) {
    party.splice(party.indexOf(dexId), 1);
    button.setAttribute('aria-pressed', 'false');
    button.setAttribute('aria-label', `Add ${name} to party`);
    button.title = `Add ${name} to party`;
    interactionStatus.textContent =
      `${name} removed from party. ${party.length} of 6 Pokémon selected.`;
  } else {
    alert('Party full (6 Pokémon). Remove one before adding another.');
    interactionStatus.textContent = 'Party full. Remove a Pokémon before adding another.';
    return;
  }

  updatePartyDisplay(currentGenerationData);

  calculateTeamWeaknesses(currentGenerationData);
});


/**
 * Removes a Pokémon from the party when its party button
 * is activated.
 */
document
  .getElementById('partyDisplay')
  .addEventListener('click', function (e) {
    const target = e.target.closest('[data-dexid]');

    if (!target) return;

    const dexId = target.dataset.dexid;
    const index = party.indexOf(dexId);

    if (index !== -1) {
      const name =
        target.querySelector('.party-info-name')?.textContent.trim();
      party.splice(index, 1);
      interactionStatus.textContent =
        `${name} removed from party. ${party.length} of 6 Pokémon selected.`;

      updatePartyDisplay(currentGenerationData);
      calculateTeamWeaknesses(currentGenerationData);
    }
  });


/**
 * Initial page setup.
 */
loadGenerations();