<?php
/**
 * Abstract Trigger
 *
 * Base class for trigger handlers providing shared functionality and dependency injection.
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Triggers;

use Exception;
use Sequensy\Engine\ExecutionGuard;
use Sequensy\Engine\FilterEvaluator;
use Sequensy\Exceptions\TriggerException;
use Sequensy\Triggers\Contracts\TriggerHandler;
use Sequensy\Traits\ErrorHandler;
use Sequensy\Storage\WorkflowStorage;
use Sequensy\Storage\ActivityLogger;

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

/**
 * Abstract class AbstractTrigger
 *
 * Provides shared functionality for trigger handlers:
 * - Dependency injection for storage, queue manager, and logger
 * - Common execute_matching_workflows() method
 */
abstract class AbstractTrigger implements TriggerHandler {

	use ErrorHandler;

	/**
	 * Workflow storage instance.
	 *
	 * @var WorkflowStorage
	 */
	protected WorkflowStorage $storage;

	/**
	 * Queue manager instance.
	 *
	 * @var object
	 */
	protected object $queue_manager;

	/**
	 * Activity logger instance.
	 *
	 * @var ActivityLogger
	 */
	protected ActivityLogger $logger;

	/**
	 * Filter evaluator instance.
	 *
	 * @var FilterEvaluator
	 */
	protected FilterEvaluator $filter_evaluator;

	/**
	 * Execution guard instance for loop prevention.
	 *
	 * @var ExecutionGuard
	 */
	protected ExecutionGuard $execution_guard;

	/**
	 * Constructor with dependency injection.
	 *
	 * All dependencies should be provided explicitly. This allows for easy
	 * testing by injecting mock dependencies.
	 *
	 * @param WorkflowStorage $storage          Workflow storage instance.
	 * @param object          $queue_manager    Queue manager instance.
	 * @param ActivityLogger  $logger           Activity logger instance.
	 * @param FilterEvaluator $filter_evaluator Filter evaluator instance.
	 * @param ExecutionGuard  $execution_guard  Execution guard instance.
	 */
	public function __construct(
		WorkflowStorage $storage,
		object $queue_manager,
		ActivityLogger $logger,
		FilterEvaluator $filter_evaluator,
		ExecutionGuard $execution_guard
	) {
		$this->storage          = $storage;
		$this->queue_manager    = $queue_manager;
		$this->logger           = $logger;
		$this->filter_evaluator = $filter_evaluator;
		$this->execution_guard  = $execution_guard;
	}

	/**
	 * Execute matching workflows for this trigger.
	 *
	 * @param array $context Trigger context data.
	 * @return void
	 */
	protected function execute_matching_workflows( array $context ): void {
		// Get all active workflows with this trigger type.
		$workflows = $this->storage->get_workflows(
			array(
				'status'       => 'active',
				'trigger_type' => $this->get_type(),
			)
		);

		$trigger_type = $this->get_type();

		// Execute each matching workflow via per-action queueing.
		foreach ( $workflows as $workflow ) {
			// Check loop prevention before attempting execution.
			$guard_result = $this->execution_guard->can_execute( $workflow->id, $context, $trigger_type );

			if ( ! $guard_result['can_execute'] ) {
				$this->logger->warning(
					$workflow->id,
					'Workflow execution blocked: loop prevention',
					array(
						'trigger_type' => $trigger_type,
						'reason'       => $guard_result['reason'],
					)
				);
				continue;
			}

			// Mark execution as started for loop detection.
			$this->execution_guard->begin_execution( $workflow->id, $context, $trigger_type );

			try {
				// Get filters from workflow config (WorkflowConfig value object).
				$config  = $workflow->draft_config;
				$trigger = $config->get_trigger();
				$filters = $trigger->get_config_value( 'filters' );

				// Evaluate filters - skip workflow if filters don't pass.
				if ( ! $this->filter_evaluator->evaluate( $filters, $context ) ) {
					$this->logger->log(
						$workflow->id,
						'info',
						'Workflow skipped: trigger filters did not match',
						array(
							'trigger_type' => $trigger_type,
							'filters'      => $filters,
						)
					);
					continue;
				}

				$execution_id = $this->queue_manager->start_execution( $workflow->id, $context );

				if ( false === $execution_id ) {
					$this->log_error(
						'Failed to start execution for workflow',
						array(
							'workflow_id'  => $workflow->id,
							'trigger_type' => $trigger_type,
						),
						$workflow->id
					);
				}
			} catch ( TriggerException $e ) {
				// Typed trigger exception - log with full context.
				$this->logger->error(
					$workflow->id,
					'Trigger execution failed: ' . $e->getMessage(),
					array_merge(
						$e->get_context(),
						array(
							'trigger_type' => $trigger_type,
							'workflow_id'  => $workflow->id,
						)
					)
				);
			} catch ( Exception $e ) {
				// Generic exception - wrap with trigger context.
				$this->logger->error(
					$workflow->id,
					'Unexpected error during trigger execution: ' . $e->getMessage(),
					array(
						'trigger_type' => $trigger_type,
						'workflow_id'  => $workflow->id,
						'exception'    => $e->getMessage(),
						'trace'        => $e->getTraceAsString(),
					)
				);
			} finally {
				// Always mark execution as ended for loop detection.
				$this->execution_guard->end_execution( $workflow->id, $context, $trigger_type );
			}
		}
	}
}
