<?php
declare(strict_types=1);

namespace WP_AUX\Traits;

/**
 * Provides utility methods for enumerations by retrieving options, generating readable lists,
 * handling errors, and attempting to find matching enum cases from provided values.
 *
 * All AUX settings with multiple possible options should be provided for in PHP "enumerators".
 * PHP enumerators used for settings are enhanced with these EnumTraits.
 *
 * @since 1.0.0
 * @link https://www.php.net/manual/en/language.enumerations.overview.php
 */
trait EnumTraits
{
  /**
   * Retrieves an array of option values for all of the cases
   * @since 1.0.0
   */
  public static function getOptions(): array
  {
    $options = array_column(self::cases(), 'value');

    return $options;
  }

  /**
   * Retrieves an associative array of options where the keys are the case values
   * and the values are either the case labels (if the "label" method exists) or the case names.
   *
   * @since 1.0.0
   */
  public static function getIndexedOptions(): array
  {
    $options = [];

    $cases = self::cases();

    foreach ($cases as $case) {
      if (method_exists($case, "label")) {
        $name = $case->label();
      } else {
        $name = $case->name;
      }

      $options[$case->value] = $name;
    }

    return $options;
  }

  /**
   * Get the comma-separated list of options for all of the cases
   * @since 1.0.0
   */
  public static function getOptionsList(): string
  {
    return "'" . join("', '", self::getOptions()) . "'";
  }

  /**
   * Generates a verbose, readable string listing of all options
   * @since 1.0.0
   */
  public static function getVerboseOptionsList(): string
  {
    $optionsList = [];

    $options = self::getOptions();
    $optionsCount = count($options);

    foreach ($options as $optionIndex => $option) {
      $or = $optionIndex === $optionsCount - 1 && $optionsCount > 1 ? "or " : "";
      $optionsList[] = $or . "'{$option}'";
    }

    return join(", ", $optionsList);
  }

  /**
   * Print a human-readable account of an error resulting from using an invalid
   * value for the enumarator
   * @since 1.0.0
   * @uses self::getVerboseOptionsList()
   */
  protected static function errorMessage(string|int $name): void
  {
    $options = self::getVerboseOptionsList();
    $field = self::SETTING_LABEL;

    print("'{$name}' is not a valid {$field} value. Try {$options}.");
  }

  /**
   * Attempts to find a case matching the provided name.
   * If the name is invalid, an error message is displayed and null is returned.
   * @since 1.0.0
   */
  public static function tryFromName(string|int $name): self|null
  {
    $enum = null;

    try {
      $enum = self::from($name);
    } catch ( \ValueError $e) {
      self::errorMessage($name);
    }

    return $enum;
  }
}
