<?php

namespace Limb_Chatbot\Includes\Data_Objects;

use Limb_Chatbot\Includes\Services\Helper;

class Parameter extends WPDB_Data_Object {

	const TABLE_NAME = 'lbaic_parameters';
	const FILLABLE = [
		'id',
		'action_id',
		'name',
		'label',
		'type',
		'required',
		'order',
		'question',
		'default_value',
		'placeholder',
		'config',
		'validation_rules',
		'suggestions'
	];

	/**
	 * Parameter type constants
	 */
	const TYPE_TEXT = 'text';
	const TYPE_TEXTAREA = 'textarea';
	const TYPE_EMAIL = 'email';
	const TYPE_PHONE = 'phone';
	const TYPE_DATE = 'date';
	const TYPE_NUMBER = 'number';
	const TYPE_ATTACHMENT = 'attachment';
	const TYPE_URL = 'url';
	const TYPE_BUTTONS = 'buttons';
	const TYPE_VECTOR_SEARCH = 'vector_search';
	const TYPE_ALIASES = [
		self::TYPE_NUMBER => [ 'number', 'integer', 'int', 'float', 'double', 'decimal', 'numeric', 'real' ],
	];

	public ?int $action_id = null;
	public ?string $name = null;
	public ?string $label = null;
	public ?string $type = null;
	public ?int $required = null;
	public ?int $order = null;
	public ?string $question = null;
	public ?string $default_value = null;
	public ?string $placeholder = null;
	public ?array $config = null;
	public ?array $validation_rules = null;
	public ?array $suggestions = null;

	/**
	 * Meta properties related to this chat.
	 *
	 * @var array
	 * @since 1.0.0
	 */
	protected array $meta_properties = [ 'search_results' ];

	/**
	 * Array of included related objects.
	 * @json_excluded
	 *
	 * @var array
	 * @since 1.0.0
	 */
	public array $included = [];

	public function __construct( $instance = null ) {
		if ( is_array( $instance ) && ! empty( $instance['validation_rules'] ) && $this->isJson( $instance['validation_rules'] ) ) {
			$instance['validation_rules'] = Helper::maybe_json_decode( $instance['validation_rules'] );
		}
		if ( is_array( $instance ) && ! empty( $instance['suggestions'] ) && $this->isJson( $instance['suggestions'] ) ) {
			$instance['suggestions'] = Helper::maybe_json_decode( $instance['suggestions'] );
		}
		if ( is_array( $instance ) && ! empty( $instance['config'] ) && $this->isJson( $instance['config'] ) ) {
			$instance['config'] = Helper::maybe_json_decode( $instance['config'] );
		}
		parent::__construct( $instance );
	}

	public function get_action_id(): ?int {
		return $this->action_id;
	}

	public function set_action_id( ?int $action_id ): void {
		$this->action_id = $action_id;
	}

	public function get_name(): ?string {
		return $this->name;
	}

	public function set_name( ?string $name ): void {
		$this->name = $name;
	}

	public function get_type(): ?string {
		return $this->type;
	}

	public function set_type( ?string $type ): void {
		$this->type = $type;
	}

	public function get_required(): ?string {
		return $this->required;
	}

	public function set_required( ?string $required ): void {
		$this->required = $required;
	}

	public function format_description() {
		$parts = [];

		// Label
		if ( $this->get_label() ) {
			$parts[] = $this->get_label();
		}

		// Default value
		if ( $default = $this->get_default_value() ) {
			$parts[] = 'Default value: ' . $default;
		}

		// Placeholder (optional)
		if ( $placeholder = $this->get_placeholder() ) {
			$parts[] = 'Placeholder: ' . $placeholder;
		}

		// Validation rules
		if ( $rules = $this->get_validation_rules( true ) ) {
			$parts[] = 'Validation rules: ' . $rules;
		}

		// Suggestions
		if ( $suggestions = $this->get_suggestions() ) {
			$parts[] = '<<< { "suggestions": ' . json_encode( $suggestions, JSON_UNESCAPED_UNICODE ) . ' } >>>';
		}

		return implode( '. ', $parts ) . '.';
	}

	public function get_label(): ?string {
		return $this->translate_parameter_string( 'label', $this->label );
	}

	public function set_label( ?string $label ): void {
		$this->label = $label;
	}

	public function get_default_value(): ?string {
		return $this->translate_parameter_string( 'default', $this->default_value );
	}

	public function set_default_value( ?string $default_value ): void {
		$this->default_value = $default_value;
	}

	public function get_placeholder(): ?string {
		return $this->translate_parameter_string( 'placeholder', $this->placeholder );
	}

	public function set_placeholder( ?string $placeholder ): void {
		$this->placeholder = $placeholder;
	}

	public function get_validation_rules( $format = false ) {
		if ( ! $format ) {
			return $this->validation_rules;
		}
		$res = '';
		foreach ( $this->validation_rules as $key => $rule ) {
			$res .= "$key: $rule,";
		}

		return rtrim( $res, ',' );
	}

	public function set_validation_rules( ?array $validation_rules ): void {
		$this->validation_rules = $validation_rules;
	}

	public function get_suggestions(): ?array {
		$suggestions = $this->suggestions;
		if ( ! is_array( $suggestions ) ) {
			return $suggestions;
		}
		foreach ( $suggestions as $index => $suggestion ) {
			$suggestions[ $index ] = $this->translate_parameter_string( sprintf( 'suggestion_%d', (int) $index ), $suggestion );
		}

		return $suggestions;
	}

	public function set_suggestions( ?array $suggestions ): void {
		$this->suggestions = $suggestions;
	}

	public function get_question(): ?string {
		return $this->translate_parameter_string( 'question', $this->question );
	}

	public function set_question( ?string $question ): void {
		$this->question = $question;
	}

	public function get_config(): ?array {
		$config = $this->config;
		if ( ! is_array( $config ) || ! isset( $config['options'] ) || ! is_array( $config['options'] ) ) {
			return $config;
		}

		foreach ( $config['options'] as $index => $option ) {
			if ( ! is_array( $option ) ) {
				continue;
			}
			if ( array_key_exists( 'label', $option ) ) {
				$config['options'][ $index ]['label'] = $this->translate_parameter_string( sprintf( 'option_%d.label', (int) $index ), $option['label'] );
			}
			if ( array_key_exists( 'tooltip', $option ) ) {
				$config['options'][ $index ]['tooltip'] = $this->translate_parameter_string( sprintf( 'option_%d.tooltip', (int) $index ), $option['tooltip'] );
			}
		}

		return $config;
	}

	public function set_config( ?array $config ): void {
		$this->config = $config;
	}

	public function get_order(): ?int {
		return $this->order;
	}

	public function set_order( ?int $order ): void {
		$this->order = $order;
	}

	public function is_searchable() {
		return in_array( $this->get_type(), [ self::TYPE_VECTOR_SEARCH ] );
	}

	public function get_config_value( $value ) {
		if ( ! is_array( $this->config ) || ! array_key_exists( $value, $this->config ) ) {
			return null;
		}

		return $this->config[ $value ];
	}

	/**
	 * Get canonical supported parameter types.
	 *
	 * @return array
	 */
	public static function get_supported_types(): array {
		return [
			self::TYPE_ATTACHMENT,
			self::TYPE_EMAIL,
			self::TYPE_DATE,
			self::TYPE_PHONE,
			self::TYPE_TEXT,
			self::TYPE_URL,
			self::TYPE_BUTTONS,
			self::TYPE_TEXTAREA,
			self::TYPE_VECTOR_SEARCH,
			self::TYPE_NUMBER,
		];
	}

	/**
	 * Get all accepted input values for parameter type, including aliases.
	 *
	 * @return array
	 */
	public static function get_supported_type_inputs(): array {
		$types = self::get_supported_types();
		foreach ( self::TYPE_ALIASES as $canonical => $aliases ) {
			$types = array_merge( $types, [ $canonical ], $aliases );
		}

		return array_values( array_unique( $types ) );
	}

	/**
	 * Normalize user-provided type to canonical internal type.
	 *
	 * @param  string|null  $type Raw type value.
	 * @return string|null
	 */
	public static function normalize_type( ?string $type ): ?string {
		if ( null === $type ) {
			return null;
		}

		$type = strtolower( trim( $type ) );
		if ( '' === $type ) {
			return '';
		}

		foreach ( self::TYPE_ALIASES as $canonical => $aliases ) {
			if ( $type === $canonical || in_array( $type, $aliases, true ) ) {
				return $canonical;
			}
		}

		return $type;
	}

	public function inject_results( array $search_results ) {
		$this->included['search_results'] = $search_results;
	}

	/**
	 * Serialize with translated public-facing parameter fields.
	 *
	 * @param int $depth Current recursion depth.
	 *
	 * @return array<string,mixed>
	 */
	public function toArray( int $depth = 0 ): array {
		$data                = parent::toArray( $depth );
		$data['label']       = $this->get_label();
		$data['question']    = $this->get_question();
		$data['default_value'] = $this->get_default_value();
		$data['placeholder'] = $this->get_placeholder();
		$data['suggestions'] = $this->get_suggestions();
		$data['config']      = $this->get_config();

		return $data;
	}

	/**
	 * Translate a parameter-facing string through integration-agnostic plugin filter.
	 *
	 * @param string $field Field suffix key.
	 * @param mixed  $value Source value.
	 *
	 * @return mixed
	 */
	private function translate_parameter_string( string $field, $value ) {
		return apply_filters(
			'lbaic_translate_dynamic_string',
			$value,
			sprintf( 'parameter.%d.%s', (int) $this->get_id(), $field ),
			[
				'scope'          => 'parameter',
				'parameter_id'   => (int) $this->get_id(),
				'action_id'      => (int) $this->get_action_id(),
				'parameter_name' => (string) $this->get_name(),
				'field'          => $field,
			]
		);
	}
}