<?php

/**
 * Custom Field Base Class
 *
 * @package AmbassadorHub\CustomFields
 */

namespace AmbassadorHub\Fields\Fields;

use AmbassadorHub\Fields\FieldFactory;

/**
 * Abstract class representing a custom field.
 */
abstract class AbstractField {

	protected string $id;
	protected string $label;
	protected array $setting_fields = array();


	/**
	 * Constructor.
	 */
	public function __construct() {
		// Intentionally empty. Hook registration is handled by the factory.
	}

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

	/**
	 * Get the field label.
	 *
	 * @return string
	 */
	public function get_label(): string {
		return $this->label;
	}

	/**
	 * Get the settings fields for this custom field.
	 *
	 * @return array
	 */
	public function get_settings_fields(): array {
		return $this->setting_fields;
	}

	/**
	 * Sanitize a single value for this field type.
	 *
	 * Override in subclasses to provide type-specific sanitization.
	 * This is used both for field values and for setting values
	 * when settings use the same field type.
	 *
	 * @param mixed $value The raw value to sanitize.
	 *
	 * @return mixed The sanitized value.
	 */
	public function sanitize_value( $value ) {
		// Default: text sanitization
		if ( is_array( $value ) ) {
			return array_map( 'sanitize_key', (array) $value );
		}
		return sanitize_key( $value );
	}

	/**
	 * Validate a single value for this field type.
	 *
	 * Override in subclasses to provide type-specific validation.
	 *
	 * @param mixed $value The value to validate.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	public function validate_value( $value ): bool { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- required to match subclass overrides of this field-type validation contract.
		// Default: always valid
		return true;
	}

	/**
	 * Sanitize field-specific settings from raw input.
	 *
	 * Delegates to each setting's field type class for proper sanitization.
	 * For example, checkbox settings are sanitized using CheckboxField::sanitize_value(),
	 * dropdown settings using DropdownField::sanitize_value(), etc.
	 *
	 * @param array $raw_settings Raw settings from $_POST or request.
	 *
	 * @return array Sanitized settings.
	 */
	public function sanitize_settings( array $raw_settings ): array {

		$sanitized       = array();
		$settings_fields = $this->get_settings_fields();

		foreach ( $settings_fields as $field ) {
			$field_id = $field['id'] ?? null;

			if ( null === $field_id || ! isset( $raw_settings[ $field_id ] ) ) {
				continue;
			}

			$value      = $raw_settings[ $field_id ];
			$field_type = $field['type'] ?? 'text';

			// Delegate sanitization to the field type class
			try {
				$field_instance         = FieldFactory::create( $field_type );
				$sanitized[ $field_id ] = $field_instance->sanitize_value( $value );
			} catch ( \InvalidArgumentException $e ) {
				// Field type not found, use default sanitization
				$sanitized[ $field_id ] = $this->sanitize_value( $value );
			}
		}

		return $sanitized;
	}

	/**
	 * Validate and process field-type-specific configuration.
	 *
	 * Override in subclasses to add custom validation logic.
	 *
	 * @param array $field Field configuration.
	 * @param array $data Raw input data.
	 *
	 * @return array Sanitized field data.
	 *
	 * @throws \InvalidArgumentException If validation fails.
	 */
	public function get_sanitized_field_data( array $data ): array {

		$field = array();

		// Validate default fields
		$field['label']       = FieldFactory::create( 'text' )->sanitize_value( $data['label'] ?? $field['label'] ?? '' );
		$field['type']        = $this->id;
		$field['placeholder'] = FieldFactory::create( 'text' )->sanitize_value( $data['placeholder'] ?? '' );
		$field['required']    = FieldFactory::create( 'checkbox' )->sanitize_value( ! empty( $data['required'] ) );

		$sanitized_settings = $this->sanitize_settings( $data['settings'] ?? array() );
		$this->validate_settings( $sanitized_settings );

		$field['settings'] = $sanitized_settings;

		return $field;
	}

	/**
	 * Validate field-specific settings.
	 *
	 * Override in subclasses to add custom validation logic for settings.
	 *
	 * @param array $settings Sanitized settings to validate.
	 *
	 * @return void
	 *
	 * @throws \InvalidArgumentException If validation fails.
	 */
	protected function validate_settings( array $settings ): void {
		// Default: no additional validation
	}

	/**
	 * Render the frontend label for this field.
	 * Override in subclasses for custom label rendering.
	 *
	 * @param array $field  The field configuration array.
	 * @param string $id    HTML id attribute for the label's "for" attribute.
	 * @param string $label The label text to display.
	 */
	public function render_field_label( array $field, $id, $label ): void {
		if ( empty( $field['label'] ) ) {
			return;
		}
		$required = ! empty( $field['required'] );
		echo '<label for="' . esc_attr( $id ) . '">'
			. esc_html( $label )
			. ( $required ? ' <span aria-hidden="true">*</span>' : '' )
			. '</label>';
	}

	/**
	 * Render the frontend input control for this field type.
	 *
	 * Override in subclasses for custom rendering.
	 *
	 * @param array  $field         Field definition.
	 * @param string $id            HTML id attribute.
	 * @param string $name          HTML name attribute.
	 * @param string $value         Current value.
	 * @param string $placeholder   Placeholder text.
	 * @param bool   $required      Whether the field is required.
	 * @param string $classes       CSS classes to apply to the input element.
	 *
	 * @return void
	 */
	public function render_field_input(
		array $field,
		string $id,
		string $name,
		string $value,
		string $placeholder,
		bool $required,
		string $classes
	): void {

		echo '<input type="text"'
			. ' id="' . esc_attr( $id ) . '"'
			. ' name="' . esc_attr( $name ) . '"'
			. ' class="' . esc_attr( $classes ) . '"'
			. ' value="' . esc_attr( $value ) . '"'
			. ' placeholder="' . esc_attr( $placeholder ) . '"'
			. ( $required ? ' required' : '' )
			. ' />';
	}

	/**
	 * Render a complete field row for frontend forms.
	 *
	 * Outputs a wrapper div containing the label, input control, optional
	 * description text, and an empty error placeholder for JS validation feedback.
	 *
	 * Subclasses may override this when the structural relationship between
	 * the label and input differs (e.g. CheckboxField wraps both in one element).
	 *
	 * @param array  $field Field definition array (label, type, settings, …).
	 * @param string $id    HTML id attribute for the input.
	 * @param string $name  HTML name attribute for the input.
	 * @param mixed  $value Current field value.
	 *
	 * @return void
	 */
	public function render_form_field(
		array $field,
		string $id,
		string $name,
		$value = ''
	): void {
		$placeholder = $field['placeholder'] ?? '';
		$required    = ! empty( $field['required'] );
		$description = $field['description'] ?? '';
		?>
		<div class="ambh-form-field ambh-form-field--<?php echo esc_attr( $this->id ); ?>">
			<?php $this->render_field_label( $field, $id, $field['label'] ?? '' ); ?>
			<?php $this->render_field_input( $field, $id, $name, (string) $value, $placeholder, $required, 'ambh-form-field__input' ); ?>
			<?php if ( $description ) : ?>
				<p class="ambh-form-field__description"><?php echo esc_html( $description ); ?></p>
			<?php endif; ?>
			<span class="ambh-form-field__error" aria-live="polite"></span>
		</div>
		<?php
	}

	/**
	 * Render field settings.
	 *
	 * @return void
	 */
	public function render_field_settings(): void {
		$settings = $this->get_settings_fields();
		if ( empty( $settings ) ) {
			return;
		}

		?>
		<fieldset class="ambh-subfield-container">
			<legend><?php echo esc_html( sprintf( '%s Settings', $this->label ) ); ?></legend>
			<?php foreach ( $settings as $field ) : ?>
				<?php $this->render_field_settings_row( $field ); ?>
			<?php endforeach; ?>
		</fieldset>
		<?php
	}

	/**
	 * Render a single field row (label + input + description).
	 *
	 * @param array $field Field configuration.
	 *
	 * @return void
	 */
	protected function render_field_settings_row( array $field ): void {
		if ( 'checkbox' !== $field['type'] && ! empty( $field['label'] ) ) {
			?>
		<label for="<?php echo esc_attr( $field['id'] ); ?>" style="display: block; margin-bottom: 4px; font-weight: 600;">
			<?php echo esc_html( $field['label'] ); ?>
		</label>
		<?php } ?>
		<?php FieldFactory::create( $field['type'] ?? 'text' )->render_field_input( $field, $field['id'], $field['id'], $field['value'] ?? '', $field['placeholder'] ?? '', $field['required'] ?? '', $field['class'] ?? '' ); ?>
		<?php if ( ! empty( $field['description'] ) ) : ?>
			<p class="description"><?php echo esc_html( $field['description'] ); ?></p>
		<?php endif; ?>
		<?php
	}
}