<?php
/**
 * Custom Fields configuration: field-type catalog, defaults, and constants.
 *
 * @package CustomFields
 * @since 1.0.0
 */

namespace CustomFields;

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

/**
 * Static configuration and catalogs for the Custom Fields module.
 */
final class Config {

	/**
	 * Storage schema version for stored field groups.
	 */
	public const SCHEMA_VERSION = 3;

	/**
	 * Maximum length of a group or field key.
	 */
	public const MAX_KEY_LENGTH = 64;

	/**
	 * Upper bounds for imported/builder definitions.
	 */
	public const MAX_FIELDS = 200;
	public const MAX_SUB_FIELDS = 100;
	public const MAX_LAYOUTS = 50;
	public const MAX_CHOICES = 500;
	public const MAX_LOCATION_RULES = 50;
	public const MAX_CONDITIONAL_RULES = 50;
	public const MAX_REPEATER_ROWS_PER_PAGE = 200;
	public const MIN_MAP_HEIGHT = 120;
	public const MAX_MAP_HEIGHT = 1200;
	public const MAX_ADMIN_LIST_COLUMNS = 20;
	public const MAX_ADMIN_LIST_ITEMS = 20;
	public const MAX_ADMIN_LIST_TEXT_LENGTH = 120;

	/**
	 * Field types that present a fixed list of choices.
	 *
	 * @return array<int, string>
	 */
	public static function choiceTypes(): array {
		return [ 'select', 'radio', 'checkbox', 'button_group' ];
	}

	/**
	 * Field types backed by a media attachment.
	 *
	 * @return array<int, string>
	 */
	public static function mediaTypes(): array {
		return [ 'image', 'file', 'gallery' ];
	}

	/**
	 * Field types that support a multiple-selection setting.
	 *
	 * @return array<int, string>
	 */
	public static function multipleTypes(): array {
		return [ 'select', 'post_object', 'page_link', 'user' ];
	}

	/**
	 * Post statuses suitable for relational field filters.
	 *
	 * @return array<string, string>
	 */
	public static function relationalPostStatuses(): array {
		$out = [];
		foreach ( get_post_stati( [ 'internal' => false ], 'objects' ) as $status ) {
			if ( ! is_object( $status ) || ! isset( $status->name ) ) {
				continue;
			}
			$name = (string) $status->name;
			if ( $name === 'acf-disabled' ) {
				continue;
			}
			$out[ $name ] = (string) $status->label;
		}

		return $out;
	}

	/**
	 * Field types that do not persist a meta value.
	 *
	 * @return array<int, string>
	 */
	public static function displayOnlyTypes(): array {
		return [ 'message', 'tab', 'accordion', 'separator', 'output' ];
	}

	/**
	 * Nested/composite field types.
	 *
	 * @return array<int, string>
	 */
	public static function nestedTypes(): array {
		return [ 'group', 'repeater', 'flexible_content', 'clone' ];
	}

	/**
	 * Relational field types that support bidirectional sync.
	 *
	 * @return array<int, string>
	 */
	public static function relationalTypes(): array {
		return [ 'post_object', 'relationship', 'term', 'user' ];
	}

	/**
	 * Whether a field type stores a value in meta.
	 *
	 * @param string $type Field type.
	 */
	public static function storesValue( string $type ): bool {
		return ! in_array( $type, self::displayOnlyTypes(), true );
	}

	/**
	 * Catalog of supported field types.
	 *
	 * @return array<string, array{label: string, group: string}>
	 */
	public static function fieldTypes(): array {
		return [
			'text'         => [ 'label' => __( 'Text', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'textarea'     => [ 'label' => __( 'Text Area', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'number'       => [ 'label' => __( 'Number', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'range'        => [ 'label' => __( 'Range', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'email'        => [ 'label' => __( 'Email', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'phone'        => [ 'label' => __( 'Phone', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'url'          => [ 'label' => __( 'URL', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'password'     => [ 'label' => __( 'Password', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'date'         => [ 'label' => __( 'Date', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'time'         => [ 'label' => __( 'Time', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'datetime'     => [ 'label' => __( 'Date & Time', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'color'        => [ 'label' => __( 'Color', 'wppowersuite-lite' ), 'group' => __( 'Basic', 'wppowersuite-lite' ) ],
			'toggle'       => [ 'label' => __( 'True / False', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'select'       => [ 'label' => __( 'Select', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'radio'        => [ 'label' => __( 'Radio Buttons', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'checkbox'     => [ 'label' => __( 'Checkboxes', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'button_group' => [ 'label' => __( 'Button Group', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'nav_menu'     => [ 'label' => __( 'Nav Menu', 'wppowersuite-lite' ), 'group' => __( 'Choice', 'wppowersuite-lite' ) ],
			'message'      => [ 'label' => __( 'Message', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'separator'    => [ 'label' => __( 'Separator', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'output'       => [ 'label' => __( 'Output', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'tab'          => [ 'label' => __( 'Tab', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'accordion'    => [ 'label' => __( 'Accordion', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'icon_picker'  => [ 'label' => __( 'Icon Picker', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'google_map'   => [ 'label' => __( 'Google Map', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'wysiwyg'      => [ 'label' => __( 'WYSIWYG Editor', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'oembed'       => [ 'label' => __( 'oEmbed', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'link'         => [ 'label' => __( 'Link', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'image'        => [ 'label' => __( 'Image', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'gallery'      => [ 'label' => __( 'Gallery', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'file'         => [ 'label' => __( 'File', 'wppowersuite-lite' ), 'group' => __( 'Content', 'wppowersuite-lite' ) ],
			'post_object'  => [ 'label' => __( 'Post Object', 'wppowersuite-lite' ), 'group' => __( 'Relational', 'wppowersuite-lite' ) ],
			'relationship' => [ 'label' => __( 'Relationship', 'wppowersuite-lite' ), 'group' => __( 'Relational', 'wppowersuite-lite' ) ],
			'page_link'    => [ 'label' => __( 'Page Link', 'wppowersuite-lite' ), 'group' => __( 'Relational', 'wppowersuite-lite' ) ],
			'term'         => [ 'label' => __( 'Taxonomy Term', 'wppowersuite-lite' ), 'group' => __( 'Relational', 'wppowersuite-lite' ) ],
			'user'             => [ 'label' => __( 'User', 'wppowersuite-lite' ), 'group' => __( 'Relational', 'wppowersuite-lite' ) ],
			'group'            => [ 'label' => __( 'Group', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'repeater'         => [ 'label' => __( 'Repeater', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'flexible_content' => [ 'label' => __( 'Flexible Content', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
			'clone'            => [ 'label' => __( 'Clone', 'wppowersuite-lite' ), 'group' => __( 'Layout', 'wppowersuite-lite' ) ],
		];
	}

	/**
	 * Which field types support each optional setting in the builder.
	 *
	 * Used by both the PHP renderer (initial visibility) and the builder JS
	 * (live visibility when the type changes) so the two never disagree.
	 *
	 * @return array<string, array<int, string>>
	 */
	public static function fieldTypeSupports(): array {
		return [
			'placeholder' => [ 'text', 'textarea', 'number', 'email', 'phone', 'url', 'password' ],
			'default'     => [ 'text', 'textarea', 'number', 'range', 'email', 'phone', 'url', 'color', 'toggle', 'select', 'radio', 'checkbox', 'button_group', 'wysiwyg' ],
			'pattern'     => [],
			'maxlength'   => [ 'text', 'textarea', 'phone' ],
			'allow_null'  => [ 'select', 'radio', 'button_group', 'nav_menu', 'post_object', 'page_link', 'term', 'user' ],
			'choice_layout' => [ 'radio', 'checkbox', 'button_group' ],
			'checkbox_toggle' => [ 'checkbox' ],
			'choice_custom' => [ 'radio', 'checkbox' ],
			'select_options' => [ 'select' ],
			'taxonomy_field_type' => [ 'term' ],
			'taxonomy_term_sync' => [ 'term' ],
			'taxonomy_term_create' => [ 'term' ],
			'user_role'   => [ 'user' ],
			'post_status_filter' => [ 'post_object', 'relationship', 'page_link' ],
			'relationship_filters' => [ 'relationship' ],
			'relationship_elements' => [ 'relationship' ],
			'allow_archives' => [ 'page_link' ],
			'media_library' => [ 'image', 'file', 'gallery' ],
			'media_mime'  => [ 'image', 'file', 'gallery' ],
			'media_size'  => [ 'image', 'file', 'gallery' ],
			'image_dimensions' => [ 'image', 'gallery' ],
			'image_preview_size' => [ 'image' ],
			'gallery_count' => [ 'gallery' ],
			'gallery_display' => [ 'gallery' ],
			'count'        => [ 'relationship', 'repeater', 'flexible_content' ],
			'button_label' => [ 'repeater', 'flexible_content' ],
			'nested_layout' => [ 'group', 'repeater' ],
			'repeater_collapsed' => [ 'repeater' ],
			'repeater_pagination' => [ 'repeater' ],
			'return_format' => [ 'select', 'radio', 'checkbox', 'button_group', 'nav_menu', 'date', 'time', 'datetime', 'color', 'link', 'image', 'file', 'gallery', 'post_object', 'relationship', 'term', 'user', 'icon_picker' ],
			'nav_menu_container' => [ 'nav_menu' ],
			'bounds'      => [ 'number', 'range' ],
			'rows'        => [ 'textarea' ],
			'new_lines'   => [ 'textarea' ],
			'date_display' => [ 'date', 'time', 'datetime' ],
			'date_save_format' => [ 'date' ],
			'date_first_day' => [ 'date', 'datetime' ],
			'default_current' => [ 'date', 'datetime' ],
			'color_options' => [ 'color' ],
			'toggle_options' => [ 'toggle' ],
			'toggle_ui' => [ 'toggle' ],
			'icon_tabs' => [ 'icon_picker' ],
			'oembed_dimensions' => [ 'oembed' ],
			'google_map_defaults' => [ 'google_map' ],
			'message_options' => [ 'message' ],
			'output_options' => [ 'output' ],
			'wysiwyg_options' => [ 'wysiwyg' ],
			'tab_options' => [ 'tab' ],
			'accordion_options' => [ 'accordion' ],
			// Prepend/append text only makes sense around an inline input
			// Keep imported prepend/append settings for these types.
			'before_after' => [ 'text', 'number', 'range', 'email', 'phone', 'password' ],
		];
	}

	/**
	 * Whether a field type supports an optional builder setting.
	 *
	 * @param string $type    Field type.
	 * @param string $setting Setting key from fieldTypeSupports().
	 */
	public static function typeSupports( string $type, string $setting ): bool {
		$map = self::fieldTypeSupports();

		return isset( $map[ $setting ] ) && in_array( $type, $map[ $setting ], true );
	}

	/**
	 * Valid return-format options per field type.
	 *
	 * @return array<string, array<string, string>>
	 */
	public static function returnFormatOptions(): array {
		return [
			'select'       => [ 'value' => __( 'Value', 'wppowersuite-lite' ), 'label' => __( 'Label', 'wppowersuite-lite' ), 'array' => __( 'Both', 'wppowersuite-lite' ) ],
			'radio'        => [ 'value' => __( 'Value', 'wppowersuite-lite' ), 'label' => __( 'Label', 'wppowersuite-lite' ), 'array' => __( 'Both', 'wppowersuite-lite' ) ],
			'checkbox'     => [ 'value' => __( 'Value', 'wppowersuite-lite' ), 'label' => __( 'Label', 'wppowersuite-lite' ), 'array' => __( 'Both', 'wppowersuite-lite' ) ],
			'button_group' => [ 'value' => __( 'Value', 'wppowersuite-lite' ), 'label' => __( 'Label', 'wppowersuite-lite' ), 'array' => __( 'Both', 'wppowersuite-lite' ) ],
			'nav_menu'     => [ 'id' => __( 'Menu ID', 'wppowersuite-lite' ), 'object' => __( 'Nav menu object', 'wppowersuite-lite' ), 'menu' => __( 'Rendered menu HTML', 'wppowersuite-lite' ) ],
			'date'         => [ 'd/m/Y' => __( 'd/m/Y', 'wppowersuite-lite' ), 'm/d/Y' => __( 'm/d/Y', 'wppowersuite-lite' ), 'F j, Y' => __( 'F j, Y', 'wppowersuite-lite' ), 'Y-m-d' => __( 'Y-m-d', 'wppowersuite-lite' ), 'Ymd' => __( 'Ymd', 'wppowersuite-lite' ) ],
			'time'         => [ 'g:i a' => __( 'g:i a', 'wppowersuite-lite' ), 'H:i' => __( 'H:i', 'wppowersuite-lite' ), 'H:i:s' => __( 'H:i:s', 'wppowersuite-lite' ) ],
			'datetime'     => [ 'd/m/Y g:i a' => __( 'd/m/Y g:i a', 'wppowersuite-lite' ), 'm/d/Y g:i a' => __( 'm/d/Y g:i a', 'wppowersuite-lite' ), 'F j, Y g:i a' => __( 'F j, Y g:i a', 'wppowersuite-lite' ), 'Y-m-d H:i' => __( 'Y-m-d H:i', 'wppowersuite-lite' ), 'Y-m-d H:i:s' => __( 'Y-m-d H:i:s', 'wppowersuite-lite' ) ],
			'color'        => [ 'string' => __( 'Color string', 'wppowersuite-lite' ), 'array' => __( 'Array', 'wppowersuite-lite' ) ],
			'link'         => [ 'array' => __( 'Array', 'wppowersuite-lite' ), 'url' => __( 'URL', 'wppowersuite-lite' ) ],
			'image'        => [ 'array' => __( 'Array', 'wppowersuite-lite' ), 'url' => __( 'URL', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'file'         => [ 'array' => __( 'Array', 'wppowersuite-lite' ), 'url' => __( 'URL', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'gallery'      => [ 'array' => __( 'Array', 'wppowersuite-lite' ), 'url' => __( 'URL', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'post_object'  => [ 'object' => __( 'Object', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'relationship' => [ 'object' => __( 'Object', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'term'         => [ 'id' => __( 'ID', 'wppowersuite-lite' ), 'object' => __( 'Object', 'wppowersuite-lite' ) ],
			'user'         => [ 'array' => __( 'Array', 'wppowersuite-lite' ), 'object' => __( 'Object', 'wppowersuite-lite' ), 'id' => __( 'ID', 'wppowersuite-lite' ) ],
			'icon_picker'  => [ 'string' => __( 'String', 'wppowersuite-lite' ), 'array' => __( 'Array', 'wppowersuite-lite' ) ],
		];
	}

	/**
	 * Location rule parameters.
	 *
	 * @return array<string, string>
	 */
	public static function locationRuleParams(): array {
		return [
			'post_type'     => __( 'Post Type', 'wppowersuite-lite' ),
			'taxonomy'      => __( 'Taxonomy', 'wppowersuite-lite' ),
			'page_template' => __( 'Page Template', 'wppowersuite-lite' ),
			'post_status'   => __( 'Post Status', 'wppowersuite-lite' ),
			'post_format'   => __( 'Post Format', 'wppowersuite-lite' ),
			'post_term'     => __( 'Post Term', 'wppowersuite-lite' ),
			'post_taxonomy' => __( 'Post Taxonomy', 'wppowersuite-lite' ),
			'post_id'       => __( 'Post ID', 'wppowersuite-lite' ),
			'post_parent'   => __( 'Post Parent ID', 'wppowersuite-lite' ),
			'user_role'     => __( 'User Role', 'wppowersuite-lite' ),
			'user_id'       => __( 'User ID', 'wppowersuite-lite' ),
			'term_id'       => __( 'Term ID', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Params requiring a saved object (skipped in relaxed matching).
	 *
	 * @return array<int, string>
	 */
	public static function strictLocationParams(): array {
		return [ 'post_id', 'post_status', 'page_template', 'post_parent', 'post_term', 'post_taxonomy', 'term_id' ];
	}

	/**
	 * Rule comparison operators.
	 *
	 * @return array<string, string>
	 */
	public static function ruleOperators(): array {
		return [
			'=='           => __( 'is equal to', 'wppowersuite-lite' ),
			'!='           => __( 'is not equal to', 'wppowersuite-lite' ),
			'>'            => __( 'is greater than', 'wppowersuite-lite' ),
			'>='           => __( 'is greater or equal', 'wppowersuite-lite' ),
			'<'            => __( 'is less than', 'wppowersuite-lite' ),
			'<='           => __( 'is less or equal', 'wppowersuite-lite' ),
			'contains'     => __( 'contains', 'wppowersuite-lite' ),
			'not_contains' => __( 'does not contain', 'wppowersuite-lite' ),
			'empty'        => __( 'is empty', 'wppowersuite-lite' ),
			'not_empty'    => __( 'is not empty', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Operators that make sense for each location-rule parameter.
	 *
	 * @return array<string, array<int, string>>
	 */
	public static function locationRuleOperators(): array {
		$choice = [ '==', '!=', 'empty', 'not_empty' ];
		$set    = [ 'contains', 'not_contains', 'empty', 'not_empty' ];
		$id     = [ '==', '!=', '>', '>=', '<', '<=', 'empty', 'not_empty' ];

		return [
			'post_type'     => $choice,
			'taxonomy'      => $choice,
			'page_template' => $choice,
			'post_status'   => $choice,
			'post_format'   => $choice,
			'post_term'     => $set,
			'post_taxonomy' => $set,
			'post_id'       => $id,
			'post_parent'   => $id,
			'user_role'     => $set,
			'user_id'       => $id,
			'term_id'       => $id,
		];
	}

	/**
	 * Operators allowed for a specific location-rule parameter.
	 *
	 * @param string $param Location-rule parameter key.
	 * @return array<string, string>
	 */
	public static function locationRuleOperatorsForParam( string $param ): array {
		$operators = self::ruleOperators();
		$allowed   = self::locationRuleOperators()[ $param ] ?? [ '==', '!=' ];

		return array_intersect_key( $operators, array_flip( $allowed ) );
	}

	/**
	 * Location-rule parameters that should use numeric inputs.
	 *
	 * @return array<int, string>
	 */
	public static function numericLocationRuleParams(): array {
		return [ 'post_id', 'post_parent', 'user_id', 'term_id' ];
	}

	/**
	 * Conditional logic operators (field value rules).
	 *
	 * @return array<string, string>
	 */
	public static function conditionalOperators(): array {
		return self::ruleOperators();
	}

	/**
	 * Whether a field type is recognized.
	 *
	 * @param string $type Field type.
	 */
	public static function isValidType( string $type ): bool {
		return array_key_exists( $type, self::fieldTypes() );
	}

	/**
	 * Custom meta box context rendered after the post title (classic editor).
	 */
	public const AFTER_TITLE_CONTEXT = 'wpps_cf_after_title';

	/**
	 * Meta-box contexts available for post-type locations.
	 *
	 * @return array<string, string>
	 */
	public static function positions(): array {
		return [
			'after_title' => __( 'Above the editor', 'wppowersuite-lite' ),
			'normal'      => __( 'Below the editor', 'wppowersuite-lite' ),
			'side'        => __( 'Editor sidebar', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Short help text for each field placement option.
	 *
	 * @return array<string, string>
	 */
	public static function positionEditorNotes(): array {
		return [
			'after_title' => __( 'Best for short fields editors should complete before writing content. In the block editor, WordPress shows this as a high-priority meta box.', 'wppowersuite-lite' ),
			'normal'      => __( 'Best for detailed field groups that need the full content column.', 'wppowersuite-lite' ),
			'side'        => __( 'Best for compact publishing metadata and simple values.', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Meta box presentation styles.
	 *
	 * @return array<string, string>
	 */
	public static function metaBoxStyles(): array {
		return [
			'default'  => __( 'Standard', 'wppowersuite-lite' ),
			'seamless' => __( 'Seamless', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Resolve a stored position to a WordPress meta box context.
	 *
	 * @param string $position Stored position key.
	 */
	public static function resolveMetaBoxContext( string $position ): string {
		if ( $position === 'after_title' ) {
			return self::AFTER_TITLE_CONTEXT;
		}

		if ( in_array( $position, [ 'normal', 'side', 'advanced' ], true ) ) {
			return $position;
		}

		return 'normal';
	}

	/**
	 * Resolve meta box priority for a context.
	 *
	 * @param string $context  Meta box context.
	 * @param string $priority Stored priority key.
	 */
	public static function resolveMetaBoxPriority( string $context, string $priority ): string {
		$priority = sanitize_key( $priority );
		if ( ! array_key_exists( $priority, self::priorities() ) ) {
			$priority = 'default';
		}

		if ( $context === 'side' && $priority === 'default' ) {
			return 'core';
		}

		if ( ( $context === self::AFTER_TITLE_CONTEXT || $context === 'normal' ) && $priority === 'default' ) {
			return 'high';
		}

		return $priority;
	}

	/**
	 * Pack fields into width-based rows (100% per row).
	 *
	 * @param array<int, array<string, mixed>> $fields Field definitions.
	 * @return array<int, array<int, array<string, mixed>>>
	 */
	public static function packFieldRows( array $fields ): array {
		$rows          = [];
		$current_row   = [];
		$current_width = 0;

		foreach ( $fields as $field ) {
			if ( ! is_array( $field ) ) {
				continue;
			}

			$width = (int) ( $field['width'] ?? 100 );
			if ( $width <= 0 || $width > 100 || ! array_key_exists( (string) $width, self::fieldWidths() ) ) {
				$width = 100;
			}

			if ( $current_row !== [] && ( $current_width + $width ) > 100 ) {
				$rows[]        = $current_row;
				$current_row   = [];
				$current_width = 0;
			}

			$current_row[]  = $field;
			$current_width += $width;

			if ( $current_width >= 100 ) {
				$rows[]        = $current_row;
				$current_row   = [];
				$current_width = 0;
			}
		}

		if ( $current_row !== [] ) {
			$rows[] = $current_row;
		}

		return $rows;
	}

	/**
	 * Meta box priority options.
	 *
	 * @return array<string, string>
	 */
	public static function priorities(): array {
		return [
			'high'    => __( 'High', 'wppowersuite-lite' ),
			'default' => __( 'Default', 'wppowersuite-lite' ),
			'low'     => __( 'Low', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Field width options (percentage of the meta box).
	 *
	 * @return array<string, string>
	 */
	public static function fieldWidths(): array {
		return [
			'100' => __( 'Full width', 'wppowersuite-lite' ),
			'75'  => __( 'Three quarters', 'wppowersuite-lite' ),
			'50'  => __( 'Half width', 'wppowersuite-lite' ),
			'33'  => __( 'One third', 'wppowersuite-lite' ),
			'25'  => __( 'Quarter width', 'wppowersuite-lite' ),
		];
	}

	/**
	 * CSS width class for a field definition.
	 *
	 * @param array<string, mixed> $field Field definition.
	 */
	public static function fieldWidthClass( array $field ): string {
		$width = (string) ( $field['width'] ?? '100' );

		return array_key_exists( $width, self::fieldWidths() ) ? 'cf-field--w' . $width : 'cf-field--w100';
	}

	/**
	 * Location target types.
	 *
	 * @return array<string, string>
	 */
	public static function locationTypes(): array {
		return [
			'post_type' => __( 'Post Type', 'wppowersuite-lite' ),
			'taxonomy'  => __( 'Taxonomy Term', 'wppowersuite-lite' ),
		];
	}

	/**
	 * Default field-group definition.
	 *
	 * @return array<string, mixed>
	 */
	public static function defaultGroup(): array {
		return [
			'key'         => '',
			'title'       => '',
			'enabled'     => true,
			'location'       => [
				'type'           => 'post_type',
				'values'         => [],
				'page_templates' => [],
			],
			'location_rules' => [
				'screen' => 'post',
				'match'  => 'all',
				'rules'  => [
					[
						'param'    => 'post_type',
						'operator' => '==',
						'value'    => 'post',
					],
				],
			],
			'position'    => 'normal',
			'priority'    => 'default',
			'style'       => 'default',
			'show_in_rest' => true,
			'fields'      => [],
			'schema'      => self::SCHEMA_VERSION,
			'created_at'  => 0,
			'updated_at'  => 0,
		];
	}

	/**
	 * Default single-field definition.
	 *
	 * @return array<string, mixed>
	 */
	public static function defaultField(): array {
		return [
			'key'             => '',
			'label'           => '',
			'name'            => '',
			'type'            => 'text',
			'instructions'    => '',
			'required'        => false,
			'default'         => '',
			'placeholder'     => '',
			'choices'         => '',
			'multiple'        => false,
			'return'          => 'id',
			'message_content' => '',
			'output_content'  => '',
			'min'             => '',
			'max'             => '',
			'step'            => '',
			'rows'            => 8,
			'new_lines'       => '',
			'post_types'      => [],
			'post_statuses'   => [],
			'relationship_filters' => [],
			'relationship_elements' => [],
			'allow_archives'  => true,
			'taxonomy'        => '',
			'taxonomy_terms'  => [],
			'taxonomy_field_type' => 'checkbox',
			'taxonomy_save_terms' => false,
			'taxonomy_load_terms' => false,
			'taxonomy_add_terms' => false,
			'user_roles'      => [],
			'media_library'   => 'all',
			'mime_types'      => '',
			'min_size'        => '',
			'max_size'        => '',
			'min_width'       => '',
			'min_height'      => '',
			'max_width'       => '',
			'max_height'      => '',
			'gallery_preview_size' => 'medium',
			'image_preview_size' => 'medium',
			'gallery_insert'  => 'append',
			'before'          => '',
			'after'           => '',
			'width'           => '100',
			'pattern'         => '',
			'maxlength'       => '',
			'allow_null'      => false,
			'choice_layout'   => 'vertical',
			'checkbox_toggle' => false,
			'allow_custom'    => false,
			'save_custom'     => false,
			'select_ui'       => false,
			'select_ajax'     => false,
			'select_create_options' => false,
			'select_save_options'   => false,
			'nav_menu_container'    => 'div',
			'show_in_list'           => false,
			'sub_fields'             => [],
			'layouts'                => [],
			'nested_layout'          => 'block',
			'collapsed'              => '',
			'rows_per_page'          => 20,
			'clone_source'           => '',
			'clone_display'          => 'seamless',
			'clone_layout'           => 'block',
			'clone_prefix_label'     => false,
			'clone_prefix_name'      => false,
			'button_label'           => '',
			'display_format'         => '',
			'save_format'            => '',
			'first_day'              => 1,
			'default_current'        => false,
			'enable_opacity'         => false,
			'custom_palette_source'  => '',
			'palette_colors'         => '',
			'show_color_wheel'       => true,
			'icon_tabs'              => [ 'dashicons', 'media_library', 'url' ],
			'toggle_message'         => '',
			'toggle_ui'              => false,
			'toggle_on_text'         => '',
			'toggle_off_text'        => '',
			'oembed_width'           => '',
			'oembed_height'          => '',
			'map_center_lat'         => '-37.81411',
			'map_center_lng'         => '144.96328',
			'map_zoom'               => '14',
			'map_height'             => '400',
			'message_new_lines'      => 'wpautop',
			'message_escape_html'    => false,
			'wysiwyg_tabs'           => 'all',
			'wysiwyg_toolbar'        => 'full',
			'wysiwyg_media_upload'   => true,
			'wysiwyg_delay'          => false,
			'tab_placement'          => 'top',
			'tab_endpoint'           => false,
			'tab_selected'           => false,
			'accordion_open'         => false,
			'accordion_multi_expand' => false,
			'accordion_endpoint'     => false,
			'conditional'            => [
				'enabled' => false,
				'match'   => 'all',
				'rules'   => [],
			],
			'bidirectional'          => false,
			'bidirectional_target'   => '',
		];
	}

	/**
	 * Default location rule row.
	 *
	 * @return array{param: string, operator: string, value: string}
	 */
	public static function defaultLocationRule(): array {
		return [
			'param'    => 'post_type',
			'operator' => '==',
			'value'    => '',
		];
	}

	/**
	 * Default conditional rule row.
	 *
	 * @return array{field: string, operator: string, value: string}
	 */
	public static function defaultConditionalRule(): array {
		return [
			'field'    => '',
			'operator' => '==',
			'value'    => '',
		];
	}

	/**
	 * Default flexible layout definition.
	 *
	 * @return array<string, mixed>
	 */
	public static function defaultLayout(): array {
		return [
			'key'        => '',
			'label'      => '',
			'sub_fields' => [],
		];
	}
}
