<?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;

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

	/**
	 * 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', 'term', 'user' ];
	}

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

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

	/**
	 * Relational field types that support bidirectional sync.
	 *
	 * @return array<int, string>
	 */
	public static function relationalTypes(): array {
		return [ 'post_object', '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', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'textarea'     => [ 'label' => __( 'Text Area', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'number'       => [ 'label' => __( 'Number', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'range'        => [ 'label' => __( 'Range', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'email'        => [ 'label' => __( 'Email', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'phone'        => [ 'label' => __( 'Phone', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'url'          => [ 'label' => __( 'URL', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'password'     => [ 'label' => __( 'Password', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'date'         => [ 'label' => __( 'Date', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'time'         => [ 'label' => __( 'Time', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'datetime'     => [ 'label' => __( 'Date & Time', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'color'        => [ 'label' => __( 'Color', 'wp-powersuite' ), 'group' => __( 'Basic', 'wp-powersuite' ) ],
			'toggle'       => [ 'label' => __( 'True / False', 'wp-powersuite' ), 'group' => __( 'Choice', 'wp-powersuite' ) ],
			'select'       => [ 'label' => __( 'Select', 'wp-powersuite' ), 'group' => __( 'Choice', 'wp-powersuite' ) ],
			'radio'        => [ 'label' => __( 'Radio Buttons', 'wp-powersuite' ), 'group' => __( 'Choice', 'wp-powersuite' ) ],
			'checkbox'     => [ 'label' => __( 'Checkboxes', 'wp-powersuite' ), 'group' => __( 'Choice', 'wp-powersuite' ) ],
			'message'      => [ 'label' => __( 'Message', 'wp-powersuite' ), 'group' => __( 'Layout', 'wp-powersuite' ) ],
			'wysiwyg'      => [ 'label' => __( 'WYSIWYG Editor', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'oembed'       => [ 'label' => __( 'oEmbed', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'link'         => [ 'label' => __( 'Link', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'image'        => [ 'label' => __( 'Image', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'gallery'      => [ 'label' => __( 'Gallery', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'file'         => [ 'label' => __( 'File', 'wp-powersuite' ), 'group' => __( 'Content', 'wp-powersuite' ) ],
			'post_object'  => [ 'label' => __( 'Post Object', 'wp-powersuite' ), 'group' => __( 'Relational', 'wp-powersuite' ) ],
			'term'         => [ 'label' => __( 'Taxonomy Term', 'wp-powersuite' ), 'group' => __( 'Relational', 'wp-powersuite' ) ],
			'user'             => [ 'label' => __( 'User', 'wp-powersuite' ), 'group' => __( 'Relational', 'wp-powersuite' ) ],
			'repeater'         => [ 'label' => __( 'Repeater', 'wp-powersuite' ), 'group' => __( 'Layout', 'wp-powersuite' ) ],
			'flexible_content' => [ 'label' => __( 'Flexible Content', 'wp-powersuite' ), 'group' => __( 'Layout', 'wp-powersuite' ) ],
			'clone'            => [ 'label' => __( 'Clone', 'wp-powersuite' ), 'group' => __( 'Layout', 'wp-powersuite' ) ],
		];
	}

	/**
	 * 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', 'oembed', 'color' ],
			'default'     => [ 'text', 'textarea', 'number', 'range', 'email', 'phone', 'url', 'password', 'date', 'time', 'datetime', 'color', 'toggle', 'select', 'radio', 'wysiwyg', 'oembed' ],
			'pattern'     => [ 'text', 'textarea', 'number', 'email', 'phone', 'url', 'password', 'oembed' ],
			'bounds'      => [ 'number', 'range', 'date', 'time', 'datetime' ],
			'rows'        => [ 'textarea', 'wysiwyg' ],
			// Prepend/append text only makes sense around an inline input
			// (matches ACF/SCF, which offer prepend/append for these types).
			'before_after' => [ 'text', 'number', 'range', 'email', 'phone', 'url', '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 );
	}

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

	/**
	 * 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', 'wp-powersuite' ),
			'!='           => __( 'is not equal to', 'wp-powersuite' ),
			'>'            => __( 'is greater than', 'wp-powersuite' ),
			'>='           => __( 'is greater or equal', 'wp-powersuite' ),
			'<'            => __( 'is less than', 'wp-powersuite' ),
			'<='           => __( 'is less or equal', 'wp-powersuite' ),
			'contains'     => __( 'contains', 'wp-powersuite' ),
			'not_contains' => __( 'does not contain', 'wp-powersuite' ),
			'empty'        => __( 'is empty', 'wp-powersuite' ),
			'not_empty'    => __( 'is not empty', 'wp-powersuite' ),
		];
	}

	/**
	 * 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 content', 'wp-powersuite' ),
			'normal'      => __( 'Below content', 'wp-powersuite' ),
			'side'        => __( 'Sidebar', 'wp-powersuite' ),
		];
	}

	/**
	 * Short help text for each field placement option.
	 *
	 * @return array<string, string>
	 */
	public static function positionEditorNotes(): array {
		return [
			'after_title' => __( 'Near the top of the edit screen, before the main content.', 'wp-powersuite' ),
			'normal'      => __( 'In the main area, below the content editor.', 'wp-powersuite' ),
			'side'        => __( 'In the sidebar. Simple fields may also appear in the document settings panel.', 'wp-powersuite' ),
		];
	}

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

	/**
	 * 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 (mirrors SCF/ACF conventions).
	 *
	 * @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', 'wp-powersuite' ),
			'default' => __( 'Default', 'wp-powersuite' ),
			'low'     => __( 'Low', 'wp-powersuite' ),
		];
	}

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

	/**
	 * 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', 'wp-powersuite' ),
			'taxonomy'  => __( 'Taxonomy Term', 'wp-powersuite' ),
		];
	}

	/**
	 * 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'  => [],
			],
			'position'    => 'normal',
			'priority'    => 'default',
			'style'       => 'default',
			'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' => '',
			'min'             => '',
			'max'             => '',
			'step'            => '',
			'rows'            => 4,
			'post_types'      => [],
			'taxonomy'        => '',
			'before'          => '',
			'after'           => '',
			'width'           => '100',
			'pattern'         => '',
			'show_in_list'           => false,
			'sub_fields'             => [],
			'layouts'                => [],
			'clone_source'           => '',
			'button_label'           => '',
			'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' => [],
		];
	}
}
