<?php
/**
 * Workflow Validator
 *
 * Facade that orchestrates workflow validation using specialized validators.
 *
 * @package Sequensy\Validation
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Validation;

use Sequensy\Validation\Concerns\CollectsErrors;
use Sequensy\Triggers\TriggerRegistry;
use Sequensy\Actions\ActionRegistry;
use Sequensy\ValueObjects\WorkflowConfig;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class WorkflowValidator
 *
 * Validates workflow configurations for activation readiness.
 * Acts as a facade, delegating to specialized validators.
 */
class WorkflowValidator {

	use CollectsErrors;

	/**
	 * Trigger registry instance.
	 *
	 * @var object
	 */
	private object $trigger_registry;

	/**
	 * Action registry instance.
	 *
	 * @var object
	 */
	private object $action_registry;

	/**
	 * Trigger validator instance.
	 *
	 * @var TriggerValidator
	 */
	private TriggerValidator $trigger_validator;

	/**
	 * Action validator instance.
	 *
	 * @var ActionValidator
	 */
	private ActionValidator $action_validator;

	/**
	 * Variable reference validator instance.
	 *
	 * @var VariableReferenceValidator
	 */
	private VariableReferenceValidator $variable_validator;

	/**
	 * Constructor.
	 *
	 * @param TriggerRegistry|null $trigger_registry Optional trigger registry instance.
	 * @param ActionRegistry|null  $action_registry  Optional action registry instance.
	 */
	public function __construct( $trigger_registry = null, $action_registry = null ) {
		$this->trigger_registry = $trigger_registry ?? TriggerRegistry::get_instance();
		$this->action_registry  = $action_registry ?? ActionRegistry::get_instance();

		$this->initialize_validators();
	}

	/**
	 * Initialize sub-validators with dependencies.
	 *
	 * @return void
	 */
	private function initialize_validators(): void {
		// Create validators with their dependencies.
		$this->trigger_validator = new TriggerValidator( $this->trigger_registry );

		$integration_validator  = new IntegrationValidator();
		$this->action_validator = new ActionValidator( $this->action_registry, $integration_validator );

		$this->variable_validator = new VariableReferenceValidator(
			$this->trigger_registry,
			$this->action_registry,
			$this->action_validator
		);
	}

	/**
	 * Validate a workflow for activation.
	 *
	 * Runs all validation checks and returns result with all errors.
	 *
	 * @param object $workflow Workflow object with config property.
	 * @return array {
	 *     Validation result.
	 *
	 *     @type bool  $valid  Whether the workflow is valid for activation.
	 *     @type array $errors Array of error objects with 'code', 'message', and optional 'context'.
	 * }
	 */
	public function validate_for_activation( object $workflow ): array {
		// Reset errors for fresh validation.
		$this->clear_errors();

		// Parse config.
		$config = $this->parse_config( $workflow->draft_config );
		if ( null === $config ) {
			$this->add_error( 'invalid_config', 'Workflow configuration must be a WorkflowConfig object.' );
			return $this->get_result();
		}

		// Run trigger validation.
		$trigger_result = $this->trigger_validator->validate( $config );
		$this->merge_errors( $trigger_result['errors'] );

		// Run action validation.
		$action_result = $this->action_validator->validate( $config );
		$this->merge_errors( $action_result['errors'] );

		// Run variable reference validation (only if we have valid actions).
		$has_actions = isset( $config['actions'] ) && is_array( $config['actions'] ) && ! empty( $config['actions'] );

		if ( $has_actions ) {
			$variable_result = $this->variable_validator->validate( $config );
			$this->merge_errors( $variable_result['errors'] );
		}

		return $this->get_result();
	}

	/**
	 * Parse workflow configuration.
	 *
	 * @param mixed $config Config as WorkflowConfig object.
	 * @return array|null Parsed config or null if not a WorkflowConfig.
	 */
	private function parse_config( mixed $config ): ?array {
		if ( ! $config instanceof WorkflowConfig ) {
			return null;
		}

		return $config->to_array();
	}
}
