<?php
/**
 * Action Config Value Object
 *
 * Value object for action configuration.
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\ValueObjects;

use WP_Error;

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

/**
 * Class ActionConfig
 *
 * Value object for action configuration.
 */
class ActionConfig implements \JsonSerializable {

	/**
	 * Action instance ID.
	 *
	 * @var string
	 */
	private string $id;

	/**
	 * Action type identifier.
	 *
	 * @var string
	 */
	private string $type;

	/**
	 * Action-specific configuration.
	 *
	 * @var array
	 */
	private array $config;

	/**
	 * User-defined label.
	 *
	 * @var string|null
	 */
	private ?string $label;

	/**
	 * Display name.
	 *
	 * @var string|null
	 */
	private ?string $name;

	/**
	 * Integration identifier.
	 *
	 * @var string|null
	 */
	private ?string $integration;

	/**
	 * Integration connection ID for multi-account support.
	 *
	 * @var string|null
	 */
	private ?string $connection_id;

	/**
	 * Next action pointer.
	 *
	 * String = single next action ID.
	 * Array = route map (route_name => action_id).
	 * Null = terminal (no next action).
	 *
	 * @var string|array<string, string>|null
	 */
	private string|array|null $next;

	/**
	 * Private constructor.
	 *
	 * @param string                            $id            Action instance ID.
	 * @param string                            $type          Action type.
	 * @param array                             $config        Configuration data.
	 * @param string|null                       $label         User label.
	 * @param string|null                       $name          Display name.
	 * @param string|null                       $integration   Integration ID.
	 * @param string|null                       $connection_id Connection ID for multi-account.
	 * @param string|array<string, string>|null $next    Next action pointer.
	 */
	private function __construct(
		string $id,
		string $type,
		array $config,
		?string $label = null,
		?string $name = null,
		?string $integration = null,
		?string $connection_id = null,
		string|array|null $next = null
	) {
		$this->id            = $id;
		$this->type          = $type;
		$this->config        = $config;
		$this->label         = $label;
		$this->name          = $name;
		$this->integration   = $integration;
		$this->connection_id = $connection_id;
		$this->next          = $next;
	}

	/**
	 * Create from array.
	 *
	 * @param array $data Action data.
	 * @return self|WP_Error Action config or error.
	 */
	public static function from_array( array $data ): self|WP_Error {
		if ( ! isset( $data['id'] ) || ! is_string( $data['id'] ) || empty( $data['id'] ) ) {
			return new WP_Error(
				'invalid_action',
				'Action must have an id.'
			);
		}

		if ( ! isset( $data['type'] ) || ! is_string( $data['type'] ) || empty( $data['type'] ) ) {
			return new WP_Error(
				'invalid_action',
				'Action must have a type.'
			);
		}

		$config        = isset( $data['config'] ) && is_array( $data['config'] ) ? $data['config'] : array();
		$label         = isset( $data['label'] ) && is_string( $data['label'] ) ? $data['label'] : null;
		$name          = isset( $data['name'] ) && is_string( $data['name'] ) ? $data['name'] : null;
		$integration   = isset( $data['integration'] ) && is_string( $data['integration'] )
			? $data['integration']
			: null;
		$connection_id = isset( $data['connection_id'] ) && is_string( $data['connection_id'] )
			? $data['connection_id']
			: null;

		$next = null;
		if ( isset( $data['next'] ) ) {
			if ( is_string( $data['next'] ) ) {
				$next = $data['next'];
			} elseif ( is_array( $data['next'] ) ) {
				$next = $data['next'];
			}
		}

		return new self( $data['id'], $data['type'], $config, $label, $name, $integration, $connection_id, $next );
	}

	/**
	 * Get action instance ID.
	 *
	 * @return string Action ID.
	 */
	public function get_id(): string {
		return $this->id;
	}

	/**
	 * Get action type.
	 *
	 * @return string Action type.
	 */
	public function get_type(): string {
		return $this->type;
	}

	/**
	 * Get configuration data.
	 *
	 * @return array Config data.
	 */
	public function get_config(): array {
		return $this->config;
	}

	/**
	 * Get a specific config value.
	 *
	 * @param string $key           Config key.
	 * @param mixed  $default_value Default value.
	 * @return mixed Config value or default.
	 */
	public function get_config_value( string $key, mixed $default_value = null ): mixed {
		return $this->config[ $key ] ?? $default_value;
	}

	/**
	 * Get user-defined label.
	 *
	 * @return string|null User label.
	 */
	public function get_label(): ?string {
		return $this->label;
	}

	/**
	 * Get display name.
	 *
	 * @return string|null Display name.
	 */
	public function get_name(): ?string {
		return $this->name;
	}

	/**
	 * Get integration identifier.
	 *
	 * @return string|null Integration ID.
	 */
	public function get_integration(): ?string {
		return $this->integration;
	}

	/**
	 * Get connection ID for multi-account support.
	 *
	 * @return string|null Connection ID.
	 */
	public function get_connection_id(): ?string {
		return $this->connection_id;
	}

	/**
	 * Get next action pointer.
	 *
	 * @return string|array<string, string>|null Next action ID, route map, or null for terminal.
	 */
	public function get_next(): string|array|null {
		return $this->next;
	}

	/**
	 * Check if this action has branching (multiple next paths).
	 *
	 * @return bool True if next is an array (route map).
	 */
	public function has_branching(): bool {
		return is_array( $this->next );
	}

	/**
	 * Convert to array.
	 *
	 * @return array Action data.
	 */
	public function to_array(): array {
		$data = array(
			'id'     => $this->id,
			'type'   => $this->type,
			'config' => $this->config,
		);

		if ( null !== $this->label ) {
			$data['label'] = $this->label;
		}

		if ( null !== $this->name ) {
			$data['name'] = $this->name;
		}

		if ( null !== $this->integration ) {
			$data['integration'] = $this->integration;
		}

		if ( null !== $this->connection_id ) {
			$data['connection_id'] = $this->connection_id;
		}

		// Always include next (even null) for graph format clarity.
		$data['next'] = $this->next;

		return $data;
	}

	/**
	 * Implement JsonSerializable for REST API responses.
	 *
	 * @return array Data to serialize.
	 */
	public function jsonSerialize(): array {
		return $this->to_array();
	}
}
