<?php
/**
 * Options Page Builder — field schema helpers.
 *
 * @package OptionsPageBuilder
 */

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

/**
 * @return array<int, string>
 */
function wpps_opb_allowed_field_types(): array {
	return [
		'text', 'textarea', 'password', 'select', 'checkbox', 'radio', 'number', 'email', 'url',
		'color', 'image', 'file', 'gallery', 'wysiwyg', 'date', 'message', 'group',
		'toggle', 'link', 'repeater',
	];
}

/**
 * Field types that store secrets and must never be returned as plaintext over REST.
 *
 * @return list<string>
 */
function wpps_opb_secret_field_types(): array {
	return [ 'password' ];
}

/**
 * Whether a field definition is treated as a secret (type or key heuristics).
 *
 * @param array<string, mixed> $field Field definition.
 */
function wpps_opb_field_is_secret( array $field ): bool {
	$type = sanitize_key( (string) ( $field['type'] ?? '' ) );
	if ( in_array( $type, wpps_opb_secret_field_types(), true ) ) {
		return true;
	}
	if ( ! empty( $field['is_secret'] ) ) {
		return true;
	}

	$id = sanitize_key( (string) ( $field['field_id'] ?? $field['id'] ?? '' ) );
	$needle = $id . ' ' . sanitize_key( (string) ( $field['name'] ?? '' ) );
	foreach ( [ 'password', 'secret', 'api_key', 'apikey', 'token', 'webhook', 'smtp', 'auth', 'private', 'credential' ] as $hint ) {
		if ( str_contains( $needle, $hint ) ) {
			return true;
		}
	}

	/**
	 * @param bool                 $is_secret Whether the field is secret.
	 * @param array<string, mixed> $field     Field definition.
	 */
	return (bool) apply_filters( 'wp_powersuite_opb_field_is_secret', false, $field );
}

/**
 * Field types allowed as repeater sub-fields.
 *
 * @return array<int, string>
 */
function wpps_opb_repeater_sub_field_types(): array {
	return [ 'text', 'textarea', 'url', 'email', 'number', 'image', 'select', 'checkbox', 'toggle' ];
}

/**
 * @param mixed $raw
 * @return array<int, array<string, mixed>>
 */
function wpps_opb_normalize_sub_fields( mixed $raw ): array {
	if ( ! is_array( $raw ) ) {
		return [];
	}

	$allowed = wpps_opb_repeater_sub_field_types();
	$out     = [];

	foreach ( $raw as $sub ) {
		if ( ! is_array( $sub ) ) {
			continue;
		}
		$name = isset( $sub['name'] ) ? sanitize_key( (string) $sub['name'] ) : '';
		if ( $name === '' ) {
			continue;
		}
		$type = isset( $sub['type'] ) ? sanitize_key( (string) $sub['type'] ) : 'text';
		if ( ! in_array( $type, $allowed, true ) ) {
			$type = 'text';
		}
		$out[] = [
			'name'    => $name,
			'label'   => isset( $sub['label'] ) ? sanitize_text_field( (string) $sub['label'] ) : ucfirst( str_replace( '_', ' ', $name ) ),
			'type'    => $type,
			'default' => isset( $sub['default'] ) ? sanitize_text_field( (string) $sub['default'] ) : '',
			'options' => isset( $sub['options'] ) ? sanitize_textarea_field( (string) $sub['options'] ) : '',
		];
	}

	return $out;
}

function wpps_opb_sanitize_layout( string $layout ): string {
	$layout  = sanitize_key( $layout );
	$allowed = [ 'stacked', 'inline', 'buttons', 'columns-2', 'columns-3' ];

	return in_array( $layout, $allowed, true ) ? $layout : 'stacked';
}

function wpps_opb_sanitize_conditional_operator( string $operator ): string {
	$operator = sanitize_key( $operator );
	$allowed  = [ 'equals', 'not_equals', 'empty', 'not_empty', 'contains' ];

	return in_array( $operator, $allowed, true ) ? $operator : 'equals';
}

function wpps_opb_sanitize_css_classes( string $classes ): string {
	$parts = preg_split( '/\s+/', trim( $classes ) ) ?: [];
	$out   = [];
	foreach ( $parts as $part ) {
		$sanitized = sanitize_html_class( $part );
		if ( $sanitized !== '' ) {
			$out[] = $sanitized;
		}
	}

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

function wpps_opb_sanitize_date_type( string $type ): string {
	$type = sanitize_key( $type );

	return in_array( $type, [ 'date', 'time', 'datetime-local' ], true ) ? $type : 'date';
}

function wpps_opb_sanitize_return_format( string $format ): string {
	$format = sanitize_key( $format );

	return in_array( $format, [ 'id', 'url' ], true ) ? $format : 'id';
}

function wpps_opb_field_stores_value( string $type ): bool {
	return ! in_array( $type, [ 'group', 'message' ], true );
}

/**
 * @param array<string, mixed> $raw
 * @return array<string, mixed>
 */
function wpps_opb_normalize_field_record( array $raw, callable $sanitize_default ): array {
	$ftype = isset( $raw['type'] ) ? sanitize_key( (string) $raw['type'] ) : 'text';
	if ( ! in_array( $ftype, wpps_opb_allowed_field_types(), true ) ) {
		$ftype = 'text';
	}

	$fid = isset( $raw['field_id'] ) && $raw['field_id'] !== ''
		? sanitize_key( (string) $raw['field_id'] )
		: ( isset( $raw['id'] ) ? sanitize_key( (string) $raw['id'] ) : '' );

	$width = isset( $raw['width'] ) ? sanitize_key( (string) $raw['width'] ) : 'full';
	if ( ! in_array( $width, [ 'full', '1-2', '1-3', '2-3' ], true ) ) {
		$width = 'full';
	}
	if ( in_array( $ftype, [ 'wysiwyg', 'image', 'file', 'message' ], true ) ) {
		$width = 'full';
	}

	return [
		'id'                   => isset( $raw['id'] ) ? sanitize_key( (string) $raw['id'] ) : '',
		'type'                 => $ftype,
		'label'                => isset( $raw['label'] ) ? sanitize_text_field( (string) $raw['label'] ) : '',
		'field_id'             => $fid,
		'description'          => isset( $raw['description'] ) ? sanitize_textarea_field( (string) $raw['description'] ) : '',
		'default'              => isset( $raw['default'] ) ? (string) $sanitize_default( $ftype, $raw['default'] ) : '',
		'options'              => isset( $raw['options'] ) ? sanitize_textarea_field( (string) $raw['options'] ) : '',
		'required'             => ! empty( $raw['required'] ),
		'readonly'             => ! empty( $raw['readonly'] ),
		'width'                => $width,
		'placeholder'          => isset( $raw['placeholder'] ) ? sanitize_text_field( (string) $raw['placeholder'] ) : '',
		'css_class'            => isset( $raw['css_class'] ) ? wpps_opb_sanitize_css_classes( (string) $raw['css_class'] ) : '',
		'maxlength'            => isset( $raw['maxlength'] ) ? absint( $raw['maxlength'] ) : 0,
		'checkbox_label'       => isset( $raw['checkbox_label'] ) ? sanitize_text_field( (string) $raw['checkbox_label'] ) : '',
		'radio_layout'         => wpps_opb_sanitize_layout( (string) ( $raw['radio_layout'] ?? '' ) ),
		'select_layout'        => wpps_opb_sanitize_layout( (string) ( $raw['select_layout'] ?? 'stacked' ) ),
		'allow_empty'          => ! isset( $raw['allow_empty'] ) || ! empty( $raw['allow_empty'] ),
		'allow_none'           => ! empty( $raw['allow_none'] ),
		'multiple'             => ! empty( $raw['multiple'] ),
		'order'                => isset( $raw['order'] ) ? absint( $raw['order'] ) : 0,
		'rows'                 => isset( $raw['rows'] ) ? absint( $raw['rows'] ) : 0,
		'min'                  => isset( $raw['min'] ) ? sanitize_text_field( (string) $raw['min'] ) : '',
		'max'                  => isset( $raw['max'] ) ? sanitize_text_field( (string) $raw['max'] ) : '',
		'step'                 => isset( $raw['step'] ) ? sanitize_text_field( (string) $raw['step'] ) : '',
		'prefix'               => isset( $raw['prefix'] ) ? sanitize_text_field( (string) $raw['prefix'] ) : '',
		'suffix'               => isset( $raw['suffix'] ) ? sanitize_text_field( (string) $raw['suffix'] ) : '',
		'media_buttons'        => ! isset( $raw['media_buttons'] ) || ! empty( $raw['media_buttons'] ),
		'teeny'                => ! empty( $raw['teeny'] ),
		'return_format'        => wpps_opb_sanitize_return_format( (string) ( $raw['return_format'] ?? 'id' ) ),
		'preview_size'         => sanitize_key( (string) ( $raw['preview_size'] ?? 'medium' ) ),
		'min_date'             => isset( $raw['min_date'] ) ? sanitize_text_field( (string) $raw['min_date'] ) : '',
		'max_date'             => isset( $raw['max_date'] ) ? sanitize_text_field( (string) $raw['max_date'] ) : '',
		'date_type'            => wpps_opb_sanitize_date_type( (string) ( $raw['date_type'] ?? 'date' ) ),
		'color_palette'        => isset( $raw['color_palette'] ) ? sanitize_textarea_field( (string) $raw['color_palette'] ) : '',
		'message_content'      => isset( $raw['message_content'] ) ? wp_kses_post( (string) $raw['message_content'] ) : '',
		'conditional_field'    => isset( $raw['conditional_field'] ) ? sanitize_key( (string) $raw['conditional_field'] ) : '',
		'conditional_operator' => wpps_opb_sanitize_conditional_operator( (string) ( $raw['conditional_operator'] ?? 'equals' ) ),
		'conditional_value'    => isset( $raw['conditional_value'] ) ? sanitize_text_field( (string) $raw['conditional_value'] ) : '',
		'sub_fields'           => wpps_opb_normalize_sub_fields( $raw['sub_fields'] ?? [] ),
		'min'                  => isset( $raw['min'] ) ? absint( $raw['min'] ) : 0,
		'max'                  => isset( $raw['max'] ) ? absint( $raw['max'] ) : 0,
		'button_label'         => isset( $raw['button_label'] ) ? sanitize_text_field( (string) $raw['button_label'] ) : '',
	];
}

/**
 * @param array<int, array<string, mixed>> $fields
 */
function wpps_opb_get_field_type_by_id( array $fields, string $field_id ): string {
	$field_id = sanitize_key( $field_id );
	if ( $field_id === '' ) {
		return 'text';
	}

	foreach ( $fields as $field ) {
		if ( ! is_array( $field ) ) {
			continue;
		}
		$fid = isset( $field['field_id'] ) && $field['field_id'] !== ''
			? sanitize_key( (string) $field['field_id'] )
			: sanitize_key( (string) ( $field['id'] ?? '' ) );
		if ( $fid === $field_id ) {
			return sanitize_key( (string) ( $field['type'] ?? 'text' ) );
		}
	}

	return 'text';
}

function wpps_opb_normalize_conditional_operand( string $value, string $field_type ): string {
	if ( ! in_array( $field_type, [ 'checkbox', 'toggle' ], true ) ) {
		return $value;
	}

	$lower = strtolower( trim( $value ) );
	if ( in_array( $lower, [ '1', 'on', 'yes', 'true', 'checked' ], true ) ) {
		return '1';
	}

	return '0';
}

function wpps_opb_evaluate_conditional( string $operator, string $actual, string $expected ): bool {
	switch ( $operator ) {
		case 'not_equals':
			return $actual !== $expected;
		case 'empty':
			return $actual === '' || $actual === '0';
		case 'not_empty':
			return $actual !== '' && $actual !== '0';
		case 'contains':
			return $expected !== '' && str_contains( $actual, $expected );
		case 'equals':
		default:
			return $actual === $expected;
	}
}

/**
 * @param array<string, mixed> $field
 * @param array<string, mixed> $values
 * @param array<int, array<string, mixed>> $all_fields
 */
function wpps_opb_field_is_visible( array $field, array $values, array $all_fields = [] ): bool {
	$dep = isset( $field['conditional_field'] ) ? sanitize_key( (string) $field['conditional_field'] ) : '';
	if ( $dep === '' ) {
		return true;
	}

	$dep_type = wpps_opb_get_field_type_by_id( $all_fields, $dep );
	$operator = wpps_opb_sanitize_conditional_operator( (string) ( $field['conditional_operator'] ?? 'equals' ) );
	$expected = wpps_opb_normalize_conditional_operand(
		(string) ( $field['conditional_value'] ?? '' ),
		$dep_type
	);
	$actual = array_key_exists( $dep, $values ) ? (string) $values[ $dep ] : '';
	$actual = wpps_opb_normalize_conditional_operand( $actual, $dep_type );

	return wpps_opb_evaluate_conditional( $operator, $actual, $expected );
}

/**
 * @param array<string, mixed> $field
 */
function wpps_opb_build_input_attributes( array $field, string $required = '' ): string {
	$parts = [];
	if ( $required !== '' ) {
		$parts[] = 'required';
	}
	if ( ! empty( $field['readonly'] ) ) {
		$parts[] = 'readonly';
	}
	if ( ! empty( $field['placeholder'] ) ) {
		$parts[] = 'placeholder="' . esc_attr( (string) $field['placeholder'] ) . '"';
	}
	if ( ! empty( $field['maxlength'] ) ) {
		$parts[] = 'maxlength="' . esc_attr( (string) absint( $field['maxlength'] ) ) . '"';
	}

	return implode( ' ', $parts );
}

/**
 * @param array<string, mixed> $field
 * @param array<int, array<string, mixed>> $all_fields
 * @return array<string, string>
 */
function wpps_opb_field_conditional_attrs( array $field, array $all_fields = [] ): array {
	if ( empty( $field['conditional_field'] ) ) {
		return [];
	}

	$dep = sanitize_key( (string) $field['conditional_field'] );

	return [
		'data-opb-conditional'   => '1',
		'data-opb-cond-field'    => $dep,
		'data-opb-cond-operator' => wpps_opb_sanitize_conditional_operator( (string) ( $field['conditional_operator'] ?? 'equals' ) ),
		'data-opb-cond-value'    => (string) ( $field['conditional_value'] ?? '' ),
		'data-opb-cond-dep-type' => wpps_opb_get_field_type_by_id( $all_fields, $dep ),
	];
}
