<?php

namespace Limb_Chatbot\Includes\Services;

use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Factories\Widget_Item_Factory;
use Limb_Chatbot\Includes\Interfaces\Chatbot_Parameter_Parser_Interface;
use Limb_Chatbot\Includes\Widgets\Items\Widget_Item;
use Limb_Chatbot\Includes\Widgets\Items\Widget_Items;
use Limb_Chatbot\Includes\Widgets\Widget;


/**
 * Parses, validates, and sanitizes chatbot widgets parameters.
 *
 * @since 1.0.0
 */
class Chatbot_Widgets_Parser implements Chatbot_Parameter_Parser_Interface {

	/**
	 * Parses an array of widget data into a Widget_Collection object.
	 *
	 * Expects flat structure with items as top-level elements.
	 *
	 * @since 1.0.0
	 *
	 * @param mixed $value Raw widget data (usually array).
	 * @param Chatbot $chatbot The chatbot instance.
	 * @return Widget_Collection|null Parsed widget collection or null if invalid.
	 */
	public function parse( $value, Chatbot $chatbot ): ?Widget_Collection {
		if ( is_array( $value ) ) {
			$items_collection = new Widget_Collection();
			// Handle flat structure (items with locations)
			foreach ( $value as $item ) {
				if ( is_array( $item ) && isset( $item['type'] ) && in_array( $item['type'], array_keys( Widget_Items::get_types() ) ) ) {
					$item_instance = $this->create_widget_item( $item );
					if ( $item_instance instanceof Widget_Item ) {
						$items_collection->push_item( $item_instance );
					}
				}
			}
		}

		return $items_collection ?? null;
	}

	/**
	 * Create a widget item instance from array data.
	 *
	 * @param array $item_data Item data array.
	 * @return Widget_Item|null Instance of the appropriate widget item class or null.
	 * @since 1.0.11
	 */
	protected function create_widget_item( array $item_data ): ?Widget_Item {
		$factory = new Widget_Item_Factory();
		$item    = $factory->make( $item_data['type'] ?? 'prompt' );

		if ( $item instanceof Widget_Item ) {
			$item->populate_properties( $item_data );
		}

		return $item;
	}

	/**
	 * Validates an array of widgets in flat structure format.
	 *
	 * @since 1.0.0
	 *
	 * @param array $widgets Array of widget items to validate.
	 * @throws Exception Throws on validation errors with appropriate error codes.
	 * @return bool True if validation passes.
	 */
	public function validate( array $widgets ) {
		// Validate flat structure (items with type and locations)
		foreach ( $widgets as $item ) {
			if ( ! is_array( $item ) ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE, __( 'Invalid widget item', 'limb-chatbot' ) );
			}
			foreach ( [ 'id', 'type', 'published', 'data' ] as $field ) {
				if ( ! isset( $item[ $field ] ) ) {
					throw new Exception( Error_Codes::VALIDATION_REQUIRED,
						__( 'Widget item fields are missing', 'limb-chatbot' ) );
				}
			}
			if ( ! in_array( $item['type'], array_keys( Widget_Items::get_types() ) ) ) {
				throw new Exception( Error_Codes::VALIDATION_UNSUPPORTED_VALUE,
					__( 'Unsupported widget item type', 'limb-chatbot' ) );
			}
			// Validate locations if present
			if ( isset( $item['locations'] ) && ! is_array( $item['locations'] ) ) {
				throw new Exception( Error_Codes::VALIDATION_INVALID_VALUE,
					__( 'Widget item must have a valid locations array', 'limb-chatbot' ) );
			}
		}

		return true;
	}

	/**
	 * Sanitizes raw widgets data into a cleaned array structure.
	 *
	 * Handles flat structure only (items with locations at top level).
	 *
	 * @since 1.0.0
	 *
	 * @param mixed $widgets Raw widget data to sanitize.
	 * @return array Sanitized widgets array.
	 */
	public function sanitize( $widgets ): array {
		$sanitized = [];

		// Sanitize flat structure
		foreach ( (array) $widgets as $item ) {
			if ( ! is_array( $item ) ) {
				continue;
			}
			$clean = [
				'id'        => isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '',
				'type'      => isset( $item['type'] ) ? sanitize_key( $item['type'] ) : '',
				'title'     => isset( $item['title'] ) ? sanitize_text_field( $item['title'] ) : '',
				'published' => isset( $item['published'] ) && $item['published'],
				'data'      => [],
				'locations' => [],
			];

			// Sanitize data
			if ( isset( $item['data'] ) && is_array( $item['data'] ) ) {
				foreach ( $item['data'] as $key => $val ) {
					if ( is_string( $val ) ) {
						if ( $item['type'] == 'text' || $item['type'] == 'lead_capture' ) {
							$clean['data'][ $key ] = wp_kses_post( $val );
						} else {
							$clean['data'][ $key ] = sanitize_text_field( $val );
						}
					} elseif ( is_array( $val ) ) {
						// Special handling for lead_capture fields array
						if ( $item['type'] === 'lead_capture' && $key === 'fields' ) {
							$clean['data'][ $key ] = $this->sanitize_lead_capture_fields( $val );
						} else {
							$clean['data'][ $key ] = wp_kses_post_deep( $val );
						}
					} elseif ( is_bool( $val ) ) {
						$clean['data'][ $key ] = $val;
					}
				}
			}

			// Sanitize locations
			if ( isset( $item['locations'] ) && is_array( $item['locations'] ) ) {
				foreach ( $item['locations'] as $group ) {
					if ( is_array( $group ) ) {
						foreach ( $group as $loc ) {
							if ( is_array( $loc ) && isset( $loc['param'], $loc['operator'], $loc['value'] ) ) {
								$clean['locations'][] = [
									[
										'param'    => sanitize_key( $loc['param'] ),
										'operator' => sanitize_text_field( $loc['operator'] ),
										'value'    => sanitize_text_field( $loc['value'] ),
									]
								];
							}
						}
					}
				}
			}

			// Sanitize suggestions if present
			if ( isset( $item['suggestions'] ) && is_array( $item['suggestions'] ) ) {
				$clean['suggestions'] = $item['suggestions'];
			}

			// Sanitize notify if present
			if ( isset( $item['notify'] ) ) {
				$clean['notify'] = $item['notify'];
			}

			// Sanitize appear_after if present
			if ( isset( $item['appear_after'] ) && is_numeric( $item['appear_after'] ) ) {
				$clean['appear_after'] = +$item['appear_after'];
			}

			$sanitized[] = $clean;
		}

		return $sanitized;
	}
	/**
	 * Sanitize lead capture form fields.
	 *
	 * @param array $fields Form fields array.
	 * @return array Sanitized fields.
	 * @since 1.0.11
	 */
	private function sanitize_lead_capture_fields( array $fields ): array {
		$sanitized_fields = [];

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

			$sanitized_field = [
				'type'        => isset( $field['type'] ) ? sanitize_key( $field['type'] ) : 'text',
				'name'        => isset( $field['name'] ) ? sanitize_text_field( $field['name'] ) : '',
				'required'    => isset( $field['required'] ) && $field['required'],
				'label'       => isset( $field['label'] ) ? sanitize_text_field( $field['label'] ) : '',
				'placeholder' => isset( $field['placeholder'] ) ? sanitize_text_field( $field['placeholder'] ) : '',
			];

			// Sanitize config field if present
			if ( isset( $field['config'] ) && is_array( $field['config'] ) ) {
				$sanitized_config = [];
				
				// Sanitize lead_capture_map_field (should be an integer Lead_Field ID)
				if ( isset( $field['config']['lead_capture_map_field'] ) ) {
					$lead_field_id = $field['config']['lead_capture_map_field'];
					if ( is_numeric( $lead_field_id ) ) {
						$sanitized_config['lead_capture_map_field'] = (int) $lead_field_id;
					}
				}
				
				if ( ! empty( $sanitized_config ) ) {
					$sanitized_field['config'] = $sanitized_config;
				}
			}

			$sanitized_fields[] = $sanitized_field;
		}

		return $sanitized_fields;
	}


}