<?php
/**
 * Centralized failure reason taxonomy for observability.
 *
 * This class defines a stable, documented vocabulary of failure reasons
 * used in observability events and error metadata. Using constants ensures
 * consistent categorization across all MCP adapter components.
 *
 * @package McpAdapter
 * @since   0.5.0
 */

declare( strict_types=1 );

namespace Bluehost\Plugin\WP\MCP\Infrastructure\Observability;

/**
 * Failure reason taxonomy for observability events.
 *
 * Categories:
 * - Registration failures: Occur during component registration at boot time
 * - Permission failures: Occur when permission checks fail at execution time
 * - Execution failures: Occur during component execution
 *
 * @since 0.5.0
 */
final class FailureReason {

	// =========================================================================
	// Registration Failures
	// =========================================================================

	/**
	 * WordPress ability was not found in the registry.
	 *
	 * Occurs when an ability name is passed to register_tools/resources/prompts
	 * but wp_get_ability() returns null.
	 */
	public const ABILITY_NOT_FOUND = 'ability_not_found';

	/**
	 * A resource with the same URI is already registered.
	 *
	 * MCP resources must have unique URIs. This failure occurs when
	 * attempting to register a duplicate.
	 */
	public const DUPLICATE_URI = 'duplicate_uri';

	/**
	 * Prompt builder threw an exception during instantiation or build.
	 *
	 * Occurs when a class implementing McpPromptBuilderInterface throws
	 * during construction or the build() method.
	 */
	public const BUILDER_EXCEPTION = 'builder_exception';

	/**
	 * Component was created without a permission callback.
	 *
	 * Domain components (McpTool, McpResource, McpPrompt) require a permission
	 * callback. This failure occurs when check_permission() is called but
	 * no callback was configured.
	 */
	public const NO_PERMISSION_STRATEGY = 'no_permission_strategy';

	/**
	 * Ability conversion returned a WP_Error.
	 *
	 * Occurs when McpTool::fromAbility(), McpResource::fromAbility(), or
	 * McpPrompt::fromAbility() fails due to invalid ability configuration.
	 */
	public const ABILITY_CONVERSION_FAILED = 'ability_conversion_failed';

	// =========================================================================
	// Permission Failures
	// =========================================================================

	/**
	 * Generic permission denied without specific reason.
	 *
	 * Used when a permission callback returns false without providing
	 * a WP_Error with more specific context.
	 */
	public const PERMISSION_DENIED = 'permission_denied';

	/**
	 * Permission callback returned a WP_Error.
	 *
	 * When the permission callback returns a WP_Error, the WP_Error code
	 * is logged separately in the 'wp_error_code' tag while this failure
	 * reason indicates the permission check explicitly failed.
	 */
	public const PERMISSION_CHECK_FAILED = 'permission_check_failed';

	// =========================================================================
	// Execution Failures
	// =========================================================================

	/**
	 * Component not found at execution time.
	 *
	 * Occurs when tools/call, resources/read, or prompts/get targets a
	 * component that doesn't exist. Different from ABILITY_NOT_FOUND which
	 * occurs at registration time.
	 */
	public const NOT_FOUND = 'not_found';

	/**
	 * Component execution returned a WP_Error.
	 *
	 * Occurs when the execute() or read() method returns a WP_Error.
	 * The WP_Error code is logged separately in 'wp_error_code'.
	 */
	public const EXECUTION_FAILED = 'execution_failed';

	/**
	 * Component execution threw an exception.
	 *
	 * Occurs when execute(), read(), or handle() throws an exception.
	 * The exception type is logged separately in 'exception_type'.
	 */
	public const EXECUTION_EXCEPTION = 'execution_exception';

	// =========================================================================
	// Validation Failures
	// =========================================================================

	/**
	 * Required parameter was missing from the request.
	 *
	 * JSON-RPC requests require certain parameters (e.g., 'name' for tools/call).
	 */
	public const MISSING_PARAMETER = 'missing_parameter';

	/**
	 * Parameter validation failed (wrong type, out of range, etc.).
	 *
	 * Occurs when input validation against schema or business rules fails.
	 */
	public const INVALID_PARAMETER = 'invalid_parameter';

	// =========================================================================
	// Helper Methods
	// =========================================================================

	/**
	 * Get all valid failure reason values.
	 *
	 * Useful for validation and documentation generation.
	 *
	 * @return array<string> List of all valid failure reason constants.
	 */
	public static function all(): array {
		return array(
			// Registration.
			self::ABILITY_NOT_FOUND,
			self::DUPLICATE_URI,
			self::BUILDER_EXCEPTION,
			self::NO_PERMISSION_STRATEGY,
			self::ABILITY_CONVERSION_FAILED,
			// Permission.
			self::PERMISSION_DENIED,
			self::PERMISSION_CHECK_FAILED,
			// Execution.
			self::NOT_FOUND,
			self::EXECUTION_FAILED,
			self::EXECUTION_EXCEPTION,
			// Validation.
			self::MISSING_PARAMETER,
			self::INVALID_PARAMETER,
		);
	}

	/**
	 * Check if a value is a valid failure reason.
	 *
	 * @param string $value The value to check.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	public static function is_valid( string $value ): bool {
		return in_array( $value, self::all(), true );
	}
}
