<?php
/**
 * Answyr Desk — configurable intake fields.
 *
 * Admin-defined questions shown on the public intake form and the portal
 * "new request" screen so agents receive context (website, versions, setup)
 * with every ticket. Definitions live in the `intake_fields` setting; each
 * submission is snapshotted (label + value) onto the ticket's meta.
 *
 * @package Answyr
 */

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

final class Answyr_Intake_Fields {

	/** Supported input types. */
	public const TYPES = array( 'text', 'url', 'number', 'textarea' );

	/**
	 * Configured field definitions.
	 *
	 * @return array<int,array{key:string,label:string,type:string,required:bool}>
	 */
	public static function all(): array {
		$raw = Answyr_Settings::get( 'intake_fields', array() );
		if ( ! is_array( $raw ) ) {
			return array();
		}
		$fields = array();
		foreach ( $raw as $field ) {
			if ( ! is_array( $field ) || empty( $field['key'] ) || empty( $field['label'] ) ) {
				continue;
			}
			$type     = isset( $field['type'] ) && in_array( $field['type'], self::TYPES, true ) ? $field['type'] : 'text';
			$fields[] = array(
				'key'      => sanitize_key( (string) $field['key'] ),
				'label'    => (string) $field['label'],
				'type'     => $type,
				'required' => ! empty( $field['required'] ),
			);
		}
		return $fields;
	}

	/** Whether any intake fields are configured. */
	public static function any(): bool {
		return array() !== self::all();
	}

	/** Definitions in a JS-friendly shape for the portal form. */
	public static function defs_for_js(): array {
		return self::all();
	}

	/**
	 * HTML inputs for the public intake form. Each control is named
	 * `field_<key>` so the front-end can collect them generically.
	 */
	public static function render_inputs(): string {
		$out = '';
		foreach ( self::all() as $field ) {
			$id       = 'answyr-field-' . $field['key'];
			$name     = 'field_' . $field['key'];
			$required = $field['required'] ? ' required' : '';
			$label    = esc_html( $field['label'] ) . ( $field['required'] ? ' <span class="answyr-req" aria-hidden="true">*</span>' : '' );

			$out .= '<p class="answyr-field"><label for="' . esc_attr( $id ) . '">' . $label . '</label>';
			if ( 'textarea' === $field['type'] ) {
				$out .= '<textarea id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '" rows="3"' . $required . '></textarea>';
			} else {
				$out .= '<input type="' . esc_attr( $field['type'] ) . '" id="' . esc_attr( $id ) . '" name="' . esc_attr( $name ) . '"' . $required . '>';
			}
			$out .= '</p>';
		}
		return $out;
	}

	/**
	 * Validate + snapshot a submission. `$submitted` is a key => raw-value map
	 * (already unslashed). Returns a { label, value } snapshot for every
	 * non-empty field, or a WP_Error naming the first missing required field.
	 *
	 * @param array<string,mixed> $submitted
	 * @return array<int,array{label:string,value:string}>|WP_Error
	 */
	public static function collect( array $submitted ) {
		$context = array();
		foreach ( self::all() as $field ) {
			$raw   = $submitted[ $field['key'] ] ?? '';
			$value = self::sanitize_value( (string) $raw, $field['type'] );

			if ( '' === $value ) {
				if ( $field['required'] ) {
					return new WP_Error(
						'answyr_field_required',
						sprintf(
							/* translators: %s: intake field label. */
							__( 'Please complete the "%s" field.', 'answyr' ),
							$field['label']
						)
					);
				}
				continue;
			}

			$context[] = array(
				'label' => $field['label'],
				'value' => $value,
			);
		}
		return $context;
	}

	private static function sanitize_value( string $raw, string $type ): string {
		$raw = trim( $raw );
		switch ( $type ) {
			case 'url':
				return esc_url_raw( $raw );
			case 'number':
				return is_numeric( $raw ) ? $raw : '';
			case 'textarea':
				return sanitize_textarea_field( $raw );
			default:
				return sanitize_text_field( $raw );
		}
	}
}
