<?php
/**
 * EtchDataLoop class for Etch plugin
 *
 * This file contains the EtchDataLoop class which handles
 * loop data structures and validation.
 *
 * @package Etch
 */

namespace Etch\Preprocessor\Data;

use Etch\Preprocessor\Utilities\EtchTypeAsserter;

/**
 * EtchDataLoop class for handling loop data.
 *
 * Supports two variants:
 * 1. {itemId?: string, loopId: string}
 * 2. {itemId?: string, targetItemId: string, targetPath: string}
 */
class EtchDataLoop {

	/**
	 * Optional item ID.
	 *
	 * @var string|null
	 */
	public $itemId;

	/**
	 * Loop ID (variant 1).
	 *
	 * @var string|null
	 */
	public $loopId;

	/**
	 * Target item ID (variant 2).
	 *
	 * @var string|null
	 */
	public $targetItemId;

	/**
	 * Target path (variant 2).
	 *
	 * @var string|null
	 */
	public $targetPath;

	/**
	 * Loop parameters given on the block.
	 *
	 * @var array<string, mixed>|null
	 */
	public $loopParams;


	/**
	 * Constructor.
	 *
	 * @param string|null               $itemId Optional item ID.
	 * @param string|null               $loopId Loop ID (variant 1).
	 * @param string|null               $targetItemId Target item ID (variant 2).
	 * @param string|null               $targetPath Target path (variant 2).
	 * @param array<string, mixed>|null $loopParams Loop parameters.
	 */
	public function __construct( $itemId = null, $loopId = null, $targetItemId = null, $targetPath = null, $loopParams = null ) {
		$this->itemId = $itemId;
		$this->loopId = $loopId;
		$this->targetItemId = $targetItemId;
		$this->targetPath = $targetPath;
		$this->loopParams = $loopParams;
	}

	/**
	 * Create EtchDataLoop from array data.
	 *
	 * @param array<string, mixed> $data Loop data array.
	 * @return self|null EtchDataLoop instance or null if invalid.
	 */
	public static function from_array( $data ) {
		if ( ! is_array( $data ) ) {
			return null;
		}

		$loopParams = isset( $data['loopParams'] ) ? EtchTypeAsserter::to_array( $data['loopParams'] ) : null;

		// Extract optional itemId
		$itemId = self::extract_string( $data, 'itemId' );

		// Check for variant 1: loopId
		$loopId = self::extract_string( $data, 'loopId' );
		if ( null !== $loopId ) {
			return new self( $itemId, $loopId, null, null, $loopParams );
		}

		// Check for variant 2: targetItemId and targetPath
		$targetItemId = self::extract_string( $data, 'targetItemId' );
		$targetPath = self::extract_string( $data, 'targetPath' );
		if ( null !== $targetItemId && null !== $targetPath ) {
			return new self( $itemId, null, $targetItemId, $targetPath, null );
		}

		// Neither variant is complete
		return null;
	}

	/**
	 * Extract string value safely.
	 *
	 * @param array<string, mixed> $data Data array.
	 * @param string               $key Key to extract.
	 * @return string|null Extracted string or null.
	 */
	private static function extract_string( $data, $key ) {
		if ( ! isset( $data[ $key ] ) ) {
			return null;
		}

		return EtchTypeAsserter::to_string_or_null( $data[ $key ] );
	}

	/**
	 * Check if this is variant 1 (loopId).
	 *
	 * @return bool True if variant 1.
	 */
	public function is_loop_id_variant() {
		return null !== $this->loopId;
	}

	/**
	 * Check if this is variant 2 (targetItemId + targetPath).
	 *
	 * @return bool True if variant 2.
	 */
	public function is_target_variant() {
		return null !== $this->targetItemId && null !== $this->targetPath;
	}

	/**
	 * Get the loop identifier based on variant.
	 *
	 * @return string|null Loop identifier.
	 */
	public function get_loop_identifier() {
		if ( $this->is_loop_id_variant() ) {
			return $this->loopId;
		}

		if ( $this->is_target_variant() ) {
			return $this->targetItemId . '.' . $this->targetPath;
		}

		return null;
	}

	/**
	 * Check if loop has an item ID.
	 *
	 * @return bool True if has item ID.
	 */
	public function has_item_id() {
		return null !== $this->itemId;
	}

	/**
	 * Convert to array representation.
	 *
	 * @return array<string, array<string, mixed>|string> Array representation.
	 */
	public function to_array() {
		$result = array();

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

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

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

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

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

		return $result;
	}

	/**
	 * Validate that the loop data is consistent.
	 *
	 * @return bool True if valid.
	 */
	public function is_valid() {
		return $this->is_loop_id_variant() || $this->is_target_variant();
	}
}
