<?php
/**
 * Select trait
 *
 * @package     AffiliateWP
 * @subpackage  Utils\Traits
 * @copyright   Copyright (c) 2026, Sandhills Development, LLC
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       2.33.0
 */

namespace AffiliateWP\Utils\Traits;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Select trait
 *
 * Renders a native <select> styled to match the AffiliateWP admin Tailwind aesthetic.
 * Opts out of the site-wide Select2 auto-init via the `affwp-ignore-select2` class so
 * the browser's native dropdown is used (and the custom chevron SVG stays visible).
 *
 * @since 2.33.0
 */
trait Select {

	/**
	 * Render a select component.
	 *
	 * @since 2.33.0
	 *
	 * @param array $args {
	 *     Select arguments.
	 *
	 *     @type string $name         Input name attribute. Required.
	 *     @type array  $options      Map of `value => label` pairs. Required.
	 *     @type string $id           Input ID attribute. Derived from name when empty.
	 *     @type string $selected     Currently selected value.
	 *     @type string $size         'sm' or 'md' (default 'md').
	 *     @type string $max_width    Tailwind max-width utility for the wrapper
	 *                                (e.g. 'max-w-sm'). Default 'max-w-sm'.
	 *                                Pass '' for no max-width.
	 *     @type string $alpine_model AlpineJS x-model binding.
	 *     @type string $class        Additional classes on the <select> element.
	 *     @type string $wrapper_class Additional classes on the grid wrapper.
	 *     @type array  $attributes   Additional HTML attributes (supports Alpine directives).
	 *     @type bool   $disabled     Whether the select is disabled.
	 * }
	 * @return string The rendered select HTML.
	 */
	public function render_select( $args = array() ) {

		$defaults = array(
			'name'          => '',
			'options'       => array(),
			'id'            => '',
			'selected'      => '',
			'size'          => 'md',
			'max_width'     => 'max-w-sm',
			'alpine_model'  => '',
			'class'         => '',
			'wrapper_class' => '',
			'attributes'    => array(),
			'disabled'      => false,
		);

		$args = wp_parse_args( $args, $defaults );

		if ( empty( $args['name'] ) || empty( $args['options'] ) || ! is_array( $args['options'] ) ) {
			return '';
		}

		if ( empty( $args['id'] ) ) {
			$args['id'] = sanitize_key( $args['name'] );
		}

		$wrapper_classes = $this->get_select_wrapper_classes( $args );
		$select_classes  = $this->get_select_classes( $args );
		$select_attrs    = $this->build_select_attributes( $args, $select_classes );
		$options_html    = $this->build_select_options( $args );

		return sprintf(
			'<div class="%s">
				<select %s>%s</select>
				%s
			</div>',
			esc_attr( $wrapper_classes ),
			$select_attrs,
			$options_html,
			\AffiliateWP\Utils\Icons::generate(
				'chevron-down',
				[ 'class' => 'col-start-1 row-start-1 mr-3 text-gray-500 pointer-events-none self-center justify-self-end size-3.5' ]
			)
		);
	}

	/**
	 * Build the wrapper classes.
	 *
	 * @since 2.33.0
	 *
	 * @param array $args Normalized select arguments.
	 * @return string
	 */
	protected function get_select_wrapper_classes( $args ) {

		$classes = array( 'grid', 'grid-cols-1', 'affwp-ignore-select2' );

		if ( ! empty( $args['max_width'] ) ) {
			$classes[] = $args['max_width'];
		}

		if ( ! empty( $args['wrapper_class'] ) ) {
			$classes[] = $args['wrapper_class'];
		}

		return implode( ' ', array_unique( $classes ) );
	}

	/**
	 * Build the <select> element classes.
	 *
	 * @since 2.33.0
	 *
	 * @param array $args Normalized select arguments.
	 * @return string
	 */
	protected function get_select_classes( $args ) {

		$classes = array(
			'col-start-1',
			'row-start-1',
			'w-full',
			'pr-10',
			'text-gray-900',
			'bg-white',
			'rounded-lg',
			'border',
			'border-gray-300',
			'transition-colors',
			'appearance-none',
			'affwp-ignore-select2',
			'focus:outline-hidden',
			'focus:border-affwp-brand-500',
			'focus:ring-1',
			'focus:ring-affwp-brand-500',
			'hover:border-gray-400',
		);

		$sizes = array(
			'sm' => array( 'py-1.5', 'pl-3', 'text-sm' ),
			'md' => array( 'py-2', 'pl-4', 'text-base' ),
		);

		$size      = isset( $sizes[ $args['size'] ] ) ? $sizes[ $args['size'] ] : $sizes['md'];
		$classes   = array_merge( $classes, $size );

		if ( $args['disabled'] ) {
			$classes[] = 'opacity-50';
			$classes[] = 'cursor-not-allowed';
		}

		if ( ! empty( $args['class'] ) ) {
			$classes[] = $args['class'];
		}

		return implode( ' ', array_unique( $classes ) );
	}

	/**
	 * Build the <select> element attribute string.
	 *
	 * @since 2.33.0
	 *
	 * @param array  $args           Normalized select arguments.
	 * @param string $select_classes Precomputed class string for the select.
	 * @return string
	 */
	protected function build_select_attributes( $args, $select_classes ) {

		$attrs = array(
			'id'    => esc_attr( $args['id'] ),
			'name'  => esc_attr( $args['name'] ),
			'class' => esc_attr( $select_classes ),
		);

		if ( ! empty( $args['alpine_model'] ) ) {
			$attrs['x-model'] = esc_attr( $args['alpine_model'] );
		}

		if ( $args['disabled'] ) {
			$attrs['disabled'] = 'disabled';
		}

		if ( ! empty( $args['attributes'] ) && is_array( $args['attributes'] ) ) {
			foreach ( $args['attributes'] as $key => $value ) {
				// Pass-through Alpine directives and event bindings verbatim.
				if ( 0 === strpos( $key, '@' ) || 0 === strpos( $key, 'x-' ) || 0 === strpos( $key, ':' ) ) {
					$attrs[ $key ] = $value;
				} else {
					$attrs[ $key ] = esc_attr( $value );
				}
			}
		}

		$pieces = array();
		foreach ( $attrs as $key => $value ) {
			if ( 'disabled' === $value ) {
				$pieces[] = $key;
			} else {
				$pieces[] = sprintf( '%s="%s"', $key, $value );
			}
		}

		return implode( ' ', $pieces );
	}

	/**
	 * Build the <option> markup for the select.
	 *
	 * @since 2.33.0
	 *
	 * @param array $args Normalized select arguments.
	 * @return string
	 */
	protected function build_select_options( $args ) {

		$html = '';

		foreach ( $args['options'] as $value => $label ) {

			$value_attr = esc_attr( (string) $value );
			$is_selected = (string) $value === (string) $args['selected'];

			$html .= sprintf(
				'<option value="%s"%s>%s</option>',
				$value_attr,
				$is_selected ? ' selected="selected"' : '',
				esc_html( (string) $label )
			);
		}

		return $html;
	}
}
