<?php
/**
 * Repeater, Flexible Content, and Clone field rendering/sanitization.
 *
 * @package CustomFields
 * @since 1.3.0
 */

namespace CustomFields;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Nested/composite field handler.
 */
final class NestedFields {

	private Fields $fields;

	public function __construct( Fields $fields ) {
		$this->fields = $fields;
	}

	/**
	 * Whether a type is a nested/composite field.
	 *
	 * @param string $type Field type.
	 */
	public static function isNestedType( string $type ): bool {
		return in_array( $type, [ 'repeater', 'flexible_content', 'clone' ], true );
	}

	/**
	 * Sub-field types allowed inside repeaters (no nesting repeaters in v1).
	 *
	 * @return array<int, string>
	 */
	public static function allowedSubFieldTypes(): array {
		$blocked = [ 'repeater', 'flexible_content', 'clone', 'message' ];

		return array_values( array_diff( array_keys( Config::fieldTypes() ), $blocked ) );
	}

	/**
	 * Render a nested field control.
	 *
	 * @param array<string, mixed> $field     Field definition.
	 * @param string               $name_attr Base name attribute.
	 * @param mixed                $value     Current value.
	 * @param string               $id_attr   Base element id.
	 * @param array<string, mixed> $group     Parent group (for clone resolution).
	 */
	public function render( array $field, string $name_attr, mixed $value, string $id_attr, array $group = [] ): void {
		$type = (string) ( $field['type'] ?? '' );

		switch ( $type ) {
			case 'repeater':
				$this->renderRepeater( $field, $name_attr, $value, $id_attr );
				break;
			case 'flexible_content':
				$this->renderFlexible( $field, $name_attr, $value, $id_attr );
				break;
			case 'clone':
				$this->renderClone( $field, $name_attr, $value, $id_attr, $group );
				break;
		}
	}

	/**
	 * Sanitize nested field value.
	 *
	 * @param array<string, mixed> $field Field definition.
	 * @param mixed                $raw   Raw submitted value.
	 * @param array<string, mixed> $group Parent group.
	 * @return mixed
	 */
	public function sanitize( array $field, mixed $raw, array $group = [] ): mixed {
		$type = (string) ( $field['type'] ?? '' );

		return match ( $type ) {
			'repeater'         => $this->sanitizeRepeater( $field, $raw ),
			'flexible_content' => $this->sanitizeFlexible( $field, $raw ),
			'clone'            => $this->sanitizeClone( $field, $raw, $group ),
			default            => [],
		};
	}

	/**
	 * @param array<string, mixed> $field     Field definition.
	 * @param string               $name_attr Name attribute.
	 * @param mixed                $value     Current rows.
	 * @param string               $id_attr   Element id prefix.
	 */
	private function renderRepeater( array $field, string $name_attr, mixed $value, string $id_attr ): void {
		$sub_fields = $this->subFields( $field );
		$rows       = is_array( $value ) ? array_values( $value ) : [];
		$min        = max( 0, (int) ( $field['min'] ?? 0 ) );
		$max        = (int) ( $field['max'] ?? 0 );
		$label      = (string) ( $field['button_label'] ?? __( 'Add Row', 'wp-powersuite' ) );

		if ( $rows === [] && $min > 0 ) {
			$rows = array_fill( 0, $min, [] );
		}

		echo '<div class="cf-repeater" data-min="' . esc_attr( (string) $min ) . '" data-max="' . esc_attr( (string) $max ) . '">';
		echo '<div class="cf-repeater__rows">';

		foreach ( $rows as $i => $row ) {
			$this->renderRepeaterRow( $sub_fields, $name_attr, is_array( $row ) ? $row : [], $id_attr, (int) $i );
		}

		echo '</div>';
		echo '<button type="button" class="button cf-repeater__add">' . esc_html( $label ) . '</button>';
		echo '<script type="text/html" class="cf-repeater-row-template">';
		$this->renderRepeaterRow( $sub_fields, $name_attr, [], $id_attr, '__ROW__' );
		echo '</script>';
		echo '</div>';
	}

	/**
	 * @param array<int, array<string, mixed>> $sub_fields Sub field definitions.
	 * @param string                           $name_attr  Parent name.
	 * @param array<string, mixed>             $row        Row values.
	 * @param string                           $id_attr    Id prefix.
	 * @param int|string                       $index      Row index.
	 */
	private function renderRepeaterRow( array $sub_fields, string $name_attr, array $row, string $id_attr, int|string $index ): void {
		echo '<div class="cf-repeater__row" data-row="' . esc_attr( (string) $index ) . '">';
		echo '<div class="cf-repeater__row-head"><span class="cf-repeater__row-label">' . esc_html__( 'Row', 'wp-powersuite' ) . ' <span class="cf-repeater__row-num"></span></span>';
		echo '<button type="button" class="button-link cf-repeater__remove">' . esc_html__( 'Remove', 'wp-powersuite' ) . '</button></div>';
		echo '<div class="cf-repeater__row-body">';

		foreach ( $sub_fields as $sub ) {
			$sub_name = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
			if ( $sub_name === '' ) {
				continue;
			}
			$sub_value = $row[ $sub_name ] ?? ( $sub['default'] ?? '' );
			$sub_id    = $id_attr . '-' . sanitize_key( (string) $index ) . '-' . sanitize_key( $sub_name );
			$input     = $name_attr . '[' . $index . '][' . $sub_name . ']';
			echo '<div class="cf-repeater__subfield">';
			$this->fields->render( $sub, $input, $sub_value, $sub_id );
			echo '</div>';
		}

		echo '</div></div>';
	}

	/**
	 * @param array<string, mixed> $field     Field definition.
	 * @param string               $name_attr Name attribute.
	 * @param mixed                $value     Rows.
	 * @param string               $id_attr   Id prefix.
	 */
	private function renderFlexible( array $field, string $name_attr, mixed $value, string $id_attr ): void {
		$layouts = $this->layouts( $field );
		$rows    = is_array( $value ) ? array_values( $value ) : [];
		$label   = (string) ( $field['button_label'] ?? __( 'Add Layout', 'wp-powersuite' ) );

		echo '<div class="cf-flexible">';
		echo '<div class="cf-flexible__rows">';

		foreach ( $rows as $i => $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$layout_key = (string) ( $row['_layout'] ?? '' );
			$layout     = $layouts[ $layout_key ] ?? null;
			if ( $layout === null ) {
				continue;
			}
			$this->renderFlexibleRow( $layout, $name_attr, $row, $id_attr, (int) $i );
		}

		echo '</div>';
		echo '<div class="cf-flexible__add-wrap">';
		echo '<select class="cf-flexible__layout-select">';
		echo '<option value="">' . esc_html__( 'Select layout…', 'wp-powersuite' ) . '</option>';
		foreach ( $layouts as $layout_key => $layout ) {
			echo '<option value="' . esc_attr( $layout_key ) . '">' . esc_html( (string) ( $layout['label'] ?? $layout_key ) ) . '</option>';
		}
		echo '</select> ';
		echo '<button type="button" class="button cf-flexible__add">' . esc_html( $label ) . '</button>';
		echo '</div>';

		foreach ( $layouts as $layout_key => $layout ) {
			echo '<script type="text/html" class="cf-flexible-row-template" data-layout="' . esc_attr( $layout_key ) . '">';
			$this->renderFlexibleRow( $layout, $name_attr, [ '_layout' => $layout_key ], $id_attr, '__ROW__' );
			echo '</script>';
		}

		echo '</div>';
	}

	/**
	 * @param array<string, mixed> $layout    Layout definition.
	 * @param string               $name_attr Parent name.
	 * @param array<string, mixed> $row       Row values.
	 * @param string               $id_attr   Id prefix.
	 * @param int|string           $index     Row index.
	 */
	private function renderFlexibleRow( array $layout, string $name_attr, array $row, string $id_attr, int|string $index ): void {
		$layout_key = (string) ( $layout['key'] ?? '' );
		$sub_fields = $this->subFields( $layout );

		echo '<div class="cf-flexible__row" data-row="' . esc_attr( (string) $index ) . '" data-layout="' . esc_attr( $layout_key ) . '">';
		echo '<div class="cf-flexible__row-head"><strong>' . esc_html( (string) ( $layout['label'] ?? $layout_key ) ) . '</strong>';
		echo '<button type="button" class="button-link cf-flexible__remove">' . esc_html__( 'Remove', 'wp-powersuite' ) . '</button></div>';
		echo '<input type="hidden" name="' . esc_attr( $name_attr . '[' . $index . '][_layout]' ) . '" value="' . esc_attr( $layout_key ) . '">';
		echo '<div class="cf-flexible__row-body">';

		foreach ( $sub_fields as $sub ) {
			$sub_name = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
			if ( $sub_name === '' ) {
				continue;
			}
			$sub_value = $row[ $sub_name ] ?? ( $sub['default'] ?? '' );
			$sub_id    = $id_attr . '-' . sanitize_key( (string) $index ) . '-' . sanitize_key( $sub_name );
			$input     = $name_attr . '[' . $index . '][' . $sub_name . ']';
			echo '<div class="cf-flexible__subfield">';
			$this->fields->render( $sub, $input, $sub_value, $sub_id );
			echo '</div>';
		}

		echo '</div></div>';
	}

	/**
	 * @param array<string, mixed> $field     Clone field.
	 * @param string               $name_attr Name attribute.
	 * @param mixed                $value     Stored value.
	 * @param string               $id_attr   Id prefix.
	 * @param array<string, mixed> $group     Parent group.
	 */
	private function renderClone( array $field, string $name_attr, mixed $value, string $id_attr, array $group ): void {
		$source = $this->resolveCloneSource( $field, $group );
		if ( $source === null ) {
			echo '<p class="description">' . esc_html__( 'Clone source field not found.', 'wp-powersuite' ) . '</p>';
			return;
		}

		if ( is_array( $source ) && isset( $source[0] ) ) {
			echo '<div class="cf-clone cf-clone--group">';
			$row = is_array( $value ) ? $value : [];
			foreach ( $source as $sub ) {
				$sub_name  = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
				$sub_value = $row[ $sub_name ] ?? ( $sub['default'] ?? '' );
				$sub_id    = $id_attr . '-' . sanitize_key( $sub_name );
				$input     = $name_attr . '[' . $sub_name . ']';
				echo '<div class="cf-clone__subfield">';
				$this->fields->render( $sub, $input, $sub_value, $sub_id );
				echo '</div>';
			}
			echo '</div>';
			return;
		}

		$this->fields->render( $source, $name_attr, $value, $id_attr );
	}

	/**
	 * @param array<string, mixed> $field Field definition.
	 * @param mixed                $raw   Raw value.
	 * @return array<int, array<string, mixed>>
	 */
	private function sanitizeRepeater( array $field, mixed $raw ): array {
		if ( ! is_array( $raw ) ) {
			return [];
		}

		$sub_fields = $this->subFields( $field );
		$max        = (int) ( $field['max'] ?? 0 );
		$clean      = [];

		foreach ( array_values( $raw ) as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$item = [];
			foreach ( $sub_fields as $sub ) {
				$sub_name = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
				if ( $sub_name === '' ) {
					continue;
				}
				$item[ $sub_name ] = $this->fields->sanitizeValue( $sub, $row[ $sub_name ] ?? null );
			}
			if ( $item !== [] ) {
				$clean[] = $item;
			}
			if ( $max > 0 && count( $clean ) >= $max ) {
				break;
			}
		}

		return $clean;
	}

	/**
	 * @param array<string, mixed> $field Field definition.
	 * @param mixed                $raw   Raw value.
	 * @return array<int, array<string, mixed>>
	 */
	private function sanitizeFlexible( array $field, mixed $raw ): array {
		if ( ! is_array( $raw ) ) {
			return [];
		}

		$layouts = $this->layouts( $field );
		$clean   = [];

		foreach ( array_values( $raw ) as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$layout_key = (string) ( $row['_layout'] ?? '' );
			$layout     = $layouts[ $layout_key ] ?? null;
			if ( $layout === null ) {
				continue;
			}
			$item = [ '_layout' => $layout_key ];
			foreach ( $this->subFields( $layout ) as $sub ) {
				$sub_name = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
				if ( $sub_name === '' ) {
					continue;
				}
				$item[ $sub_name ] = $this->fields->sanitizeValue( $sub, $row[ $sub_name ] ?? null );
			}
			$clean[] = $item;
		}

		return $clean;
	}

	/**
	 * @param array<string, mixed> $field Field definition.
	 * @param mixed                $raw   Raw value.
	 * @param array<string, mixed> $group Parent group.
	 * @return mixed
	 */
	private function sanitizeClone( array $field, mixed $raw, array $group ): mixed {
		$source = $this->resolveCloneSource( $field, $group );
		if ( $source === null ) {
			return [];
		}

		if ( is_array( $source ) && isset( $source[0] ) ) {
			if ( ! is_array( $raw ) ) {
				return [];
			}
			$out = [];
			foreach ( $source as $sub ) {
				$sub_name = (string) ( $sub['name'] ?? $sub['key'] ?? '' );
				if ( $sub_name === '' ) {
					continue;
				}
				$out[ $sub_name ] = $this->fields->sanitizeValue( $sub, $raw[ $sub_name ] ?? null );
			}
			return $out;
		}

		return $this->fields->sanitizeValue( $source, $raw );
	}

	/**
	 * @param array<string, mixed> $field Field or layout.
	 * @return array<int, array<string, mixed>>
	 */
	private function subFields( array $field ): array {
		$raw = isset( $field['sub_fields'] ) && is_array( $field['sub_fields'] ) ? $field['sub_fields'] : [];
		$out = [];
		foreach ( $raw as $sub ) {
			if ( is_array( $sub ) ) {
				$out[] = $sub;
			}
		}

		return $out;
	}

	/**
	 * @param array<string, mixed> $field Field definition.
	 * @return array<string, array<string, mixed>>
	 */
	private function layouts( array $field ): array {
		$raw = isset( $field['layouts'] ) && is_array( $field['layouts'] ) ? $field['layouts'] : [];
		$out = [];
		foreach ( $raw as $layout ) {
			if ( ! is_array( $layout ) ) {
				continue;
			}
			$key = (string) ( $layout['key'] ?? '' );
			if ( $key !== '' ) {
				$out[ $key ] = $layout;
			}
		}

		return $out;
	}

	/**
	 * Resolve clone source field(s).
	 *
	 * @param array<string, mixed> $field Clone field.
	 * @param array<string, mixed> $group Parent group.
	 * @return array<string, mixed>|array<int, array<string, mixed>>|null
	 */
	private function resolveCloneSource( array $field, array $group ): array|null {
		$source = (string) ( $field['clone_source'] ?? '' );
		if ( $source === '' ) {
			return null;
		}

		if ( str_contains( $source, '|' ) ) {
			[ $group_key, $field_key ] = array_pad( explode( '|', $source, 2 ), 2, '' );
			$storage = new Storage();
			$remote  = $storage->get( sanitize_key( $group_key ) );
			if ( ! is_array( $remote ) ) {
				return null;
			}
			return $this->findFieldByKey( $remote, sanitize_key( $field_key ) );
		}

		if ( str_contains( $source, ',' ) ) {
			$keys   = array_map( 'trim', explode( ',', $source ) );
			$fields = [];
			foreach ( $keys as $field_key ) {
				$found = $this->findFieldByKey( $group, sanitize_key( $field_key ) );
				if ( is_array( $found ) && ! isset( $found[0] ) ) {
					$fields[] = $found;
				}
			}

			return $fields !== [] ? $fields : null;
		}

		return $this->findFieldByKey( $group, sanitize_key( $source ) );
	}

	/**
	 * @param array<string, mixed> $group     Group definition.
	 * @param string               $field_key Field key.
	 * @return array<string, mixed>|null
	 */
	private function findFieldByKey( array $group, string $field_key ): ?array {
		foreach ( (array) ( $group['fields'] ?? [] ) as $f ) {
			if ( is_array( $f ) && (string) ( $f['key'] ?? '' ) === $field_key ) {
				return $f;
			}
		}

		return null;
	}
}
