Source: fetch.js

/**
 * @file fetch.js
 * @description Handles loading, validating, filtering, and rendering Pokémon data.
 *
 * - `fetchGenerations()` retrieves the supported generation list.
 * - `fetchDexEntries()` retrieves and validates generation data.
 * - `ViewPokemon()` filters and displays Pokémon based on user-selected criteria.
 */
import {
  selectedSprites,
  selectedVersion,
  selectedEvolution,
  selectedLegendary,
  selectedEvolve,
  selectedUnobtainable,
  results
} from './state.js';

/**
 * Fetches the list of supported generations.
 *
 * @returns {Promise<Object>}
 */
export async function fetchGenerations() {
  const response = await fetch('/data/generations.json');

  if (!response.ok) {
    throw new Error('Unable to load the generation list.');
  }

  try {
    const data = await response.json();
    return validateGenerationList(data);
  } catch (error) {
    if (error instanceof SyntaxError) {
      throw new Error(
        'The generation list contains invalid JSON.'
      );
    }

    throw error;
  }
}

/**
 * Validates required fields in generation data.
 *
 * @param {Object} data - Parsed generation JSON data
 * @returns {Object} Validated generation data
 * @throws {Error} If required generation data is missing
 */
function validateGenerationData(data) {
  if (
    !data ||
    typeof data !== 'object' ||
    typeof data.displayName !== 'string' ||
    typeof data.description !== 'string' ||
    typeof data.imageSrc !== 'string' ||
    !Array.isArray(data.spriteSets) ||
    !Array.isArray(data.versions) ||
    !Array.isArray(data.pokemon)
  ) {
    throw new Error(
      'The selected generation data is missing required information.'
    );
  }

  return data;
}

/**
 * Validates the supported generation list.
 *
 * @param {Object} data - Parsed generation JSON data
 * @returns {Object} Validated generation data
 * @throws {Error} If required generation data is missing
 */
function validateGenerationList(data) {
  if (
    !data ||
    typeof data !== 'object' ||
    !Array.isArray(data.generations)
  ) {
    throw new Error(
      'The generation list is missing required information.'
    );
  }
  return data;
}

/**
 * Fetches data for a selected Pokémon generation.
 *
 * @param {string} file - JSON filename for the selected generation
 * @returns {Promise<Object>}
 */
export async function fetchDexEntries(file) {
  const response = await fetch(`/data/${file}`);

  if (!response.ok) {
    throw new Error(`Unable to load ${file}.`);
  }

  try {
    const data = await response.json();
    return validateGenerationData(data);
  } catch (error) {
    if (error instanceof SyntaxError) {
      throw new Error(`The data in ${file} is not valid JSON.`);
    }

    throw error;
  }
}

/**
 * Renders filtered Pokémon sprites based on selected version, evolution, and other options.
 * Appends the images to the results container.
 *
 * @param {Object} dexResults - The full Dex dataset including image base path and Pokémon list
 * @returns {void}
 */
export function ViewPokemon(dexResults) {
  const imageSrc =
    dexResults['imageSrc'] +
    selectedSprites.value +
    '/transparent/';

  const pokemon = dexResults['pokemon'];
  const htmlPosition = 'beforeend';

  let matchingPokemon = 0;

  results.innerHTML = '';

  for (const entry in pokemon) {
    const dexId = pokemon[entry]['dexId'];
    const tradeEvolve = pokemon[entry]['evolve']['trade'];
    const name = pokemon[entry]['name'];
    const finalForm = pokemon[entry]['finalForm'];
    const isLegendary = pokemon[entry]['isLegendary'];
    const types = pokemon[entry]['types'].join();
    const unobtainable = pokemon[entry]['unobtainable'];

    const imageName = dexId.replace(/^0+/, '');
    const pokedexResults = `
      <button
        type="button"
        class="pokemon-result"
        data-name="${name}"
        data-types="${types}"
        data-pokdex="${dexId}"
        aria-pressed="false"
        aria-label="Add ${name} to party"
        title="Add ${name} to party"
      >
        <img
          src="${imageSrc + imageName}.png"
          alt=""
        >
        <span class="pokemon-result-name">${name}</span>
      </button>
    `;

    if (selectedLegendary.value === 'no' && isLegendary) continue;
    if (selectedEvolve.value === 'yes' && tradeEvolve) continue;

    if (
      selectedUnobtainable.value === 'yes' &&
      unobtainable.includes(selectedVersion.value)
    ) {
      continue;
    }

    if (selectedEvolution.value === 'yes' && !finalForm) continue;

    matchingPokemon += 1;

    results.insertAdjacentHTML(htmlPosition, pokedexResults);
  }

  results.insertAdjacentHTML(
    'afterbegin',
    `<h2>Available Pokémon (${matchingPokemon})</h2>`
  );
}