<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\Exception;

/**
 * Exception thrown when parameter validation fails.
 *
 * This exception is thrown by the ParameterResolver when a parameter
 * value fails schema validation (type mismatch, enum violation,
 * constraint violation, etc.).
 *
 * @see \Drupal\flowdrop_runtime\Service\ParameterResolver
 */
class ParameterValidationException extends RuntimeException {

  /**
   * The name of the invalid parameter.
   *
   * @var string
   */
  protected string $parameterName;

  /**
   * The invalid value that was provided.
   *
   * @var mixed
   */
  protected mixed $invalidValue;

  /**
   * The validation constraint that failed.
   *
   * @var string
   */
  protected string $constraint;

  /**
   * Constructs a ParameterValidationException.
   *
   * @param string $parameterName
   *   The name of the invalid parameter.
   * @param mixed $invalidValue
   *   The invalid value.
   * @param string $constraint
   *   The constraint that failed (e.g., "type", "enum", "minimum").
   * @param string $message
   *   The exception message.
   * @param int $code
   *   The exception code.
   * @param \Throwable|null $previous
   *   The previous exception.
   */
  public function __construct(
    string $parameterName,
    mixed $invalidValue,
    string $constraint,
    string $message = "",
    int $code = 0,
    ?\Throwable $previous = NULL,
  ) {
    $this->parameterName = $parameterName;
    $this->invalidValue = $invalidValue;
    $this->constraint = $constraint;

    if (empty($message)) {
      $valueStr = is_scalar($invalidValue) ? (string) $invalidValue : gettype($invalidValue);
      $message = "Parameter '{$parameterName}' failed validation: {$constraint} (value: {$valueStr})";
    }

    parent::__construct($message, $code, $previous);
  }

  /**
   * Get the name of the invalid parameter.
   *
   * @return string
   *   The parameter name.
   */
  public function getParameterName(): string {
    return $this->parameterName;
  }

  /**
   * Get the invalid value.
   *
   * @return mixed
   *   The invalid value.
   */
  public function getInvalidValue(): mixed {
    return $this->invalidValue;
  }

  /**
   * Get the constraint that failed.
   *
   * @return string
   *   The constraint name.
   */
  public function getConstraint(): string {
    return $this->constraint;
  }

}
