<?php

declare(strict_types=1);

namespace Drupal\flowdrop\DTO;

/**
 * Represents the result of parameter validation.
 *
 * Provides a structured way to return validation results from node processors,
 * replacing the simple boolean return type. This allows:
 * - Detailed error messages for each validation failure
 * - Warnings for non-critical issues
 * - Centralized logging by the orchestration layer.
 *
 * @see \Drupal\flowdrop\Plugin\FlowDropNodeProcessor\FlowDropNodeProcessorInterface::validateParams()
 */
final class ValidationResult {

  /**
   * Constructs a ValidationResult.
   *
   * @param bool $valid
   *   Whether validation passed.
   * @param array<int, ValidationError> $errors
   *   Validation errors that caused failure.
   * @param array<int, ValidationWarning> $warnings
   *   Validation warnings (non-blocking issues).
   */
  public function __construct(
    private readonly bool $valid,
    private readonly array $errors = [],
    private readonly array $warnings = [],
  ) {}

  /**
   * Create a successful validation result.
   *
   * @param array<int, ValidationWarning> $warnings
   *   Optional warnings to include despite success.
   *
   * @return self
   *   A valid result.
   */
  public static function success(array $warnings = []): self {
    return new self(valid: TRUE, errors: [], warnings: $warnings);
  }

  /**
   * Create a failed validation result.
   *
   * @param array<int, ValidationError> $errors
   *   The validation errors.
   * @param array<int, ValidationWarning> $warnings
   *   Optional warnings to include.
   *
   * @return self
   *   An invalid result.
   */
  public static function failure(array $errors, array $warnings = []): self {
    return new self(valid: FALSE, errors: $errors, warnings: $warnings);
  }

  /**
   * Create a failed result from a single error message.
   *
   * @param string $parameter
   *   The parameter name that failed validation.
   * @param string $message
   *   The error message.
   * @param string|null $code
   *   Optional error code.
   *
   * @return self
   *   An invalid result.
   */
  public static function error(string $parameter, string $message, ?string $code = NULL): self {
    return new self(
      valid: FALSE,
      errors: [new ValidationError($parameter, $message, $code)],
    );
  }

  /**
   * Check if validation passed.
   *
   * @return bool
   *   TRUE if validation passed.
   */
  public function isValid(): bool {
    return $this->valid;
  }

  /**
   * Get validation errors.
   *
   * @return array<int, ValidationError>
   *   The validation errors.
   */
  public function getErrors(): array {
    return $this->errors;
  }

  /**
   * Get validation warnings.
   *
   * @return array<int, ValidationWarning>
   *   The validation warnings.
   */
  public function getWarnings(): array {
    return $this->warnings;
  }

  /**
   * Check if there are any errors.
   *
   * @return bool
   *   TRUE if there are errors.
   */
  public function hasErrors(): bool {
    return count($this->errors) > 0;
  }

  /**
   * Check if there are any warnings.
   *
   * @return bool
   *   TRUE if there are warnings.
   */
  public function hasWarnings(): bool {
    return count($this->warnings) > 0;
  }

  /**
   * Get error count.
   *
   * @return int
   *   Number of errors.
   */
  public function getErrorCount(): int {
    return count($this->errors);
  }

  /**
   * Get warning count.
   *
   * @return int
   *   Number of warnings.
   */
  public function getWarningCount(): int {
    return count($this->warnings);
  }

  /**
   * Get all error messages as a flat array.
   *
   * @return array<int, string>
   *   Array of error messages.
   */
  public function getErrorMessages(): array {
    return array_map(
      fn(ValidationError $error): string => $error->getMessage(),
      $this->errors
    );
  }

  /**
   * Get all warning messages as a flat array.
   *
   * @return array<int, string>
   *   Array of warning messages.
   */
  public function getWarningMessages(): array {
    return array_map(
      fn(ValidationWarning $warning): string => $warning->getMessage(),
      $this->warnings
    );
  }

  /**
   * Convert to array representation.
   *
   * @return array{valid: bool, errors: array<int, array{parameter: string, message: string, code: string|null}>, warnings: array<int, array{parameter: string, message: string}>}
   *   Array representation of the validation result.
   */
  public function toArray(): array {
    return [
      "valid" => $this->valid,
      "errors" => array_map(
        fn(ValidationError $error): array => $error->toArray(),
        $this->errors
      ),
      "warnings" => array_map(
        fn(ValidationWarning $warning): array => $warning->toArray(),
        $this->warnings
      ),
    ];
  }

  /**
   * Merge with another validation result.
   *
   * @param \Drupal\flowdrop\DTO\ValidationResult $other
   *   The other validation result to merge.
   *
   * @return self
   *   A new merged result.
   */
  public function merge(ValidationResult $other): self {
    return new self(
      valid: $this->valid && $other->valid,
      errors: array_merge($this->errors, $other->errors),
      warnings: array_merge($this->warnings, $other->warnings),
    );
  }

}
