<?php
/**
 * Content plan normalization service.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Normalizes legacy and dynamic editable content plans.
 */
class SmartyPress_AI_Content_Generator_Plan_Normalizer {

	/**
	 * Check whether a plan has the dynamic schema/data shape.
	 *
	 * @param array $plan Content plan.
	 * @return bool
	 */
	public function is_dynamic_post_plan( $plan ) {
		return is_array( $plan )
			&& isset( $plan['schema'], $plan['data'] )
			&& is_array( $plan['schema'] )
			&& is_array( $plan['data'] );
	}

	/**
	 * Normalize any supported plan shape.
	 *
	 * @param array $plan Content plan.
	 * @return array
	 */
	public function normalize_post_plan( $plan ) {
		$defaults = $this->get_default_post_plan();
		$plan     = is_array( $plan ) ? $plan : array();

		if ( $this->is_dynamic_post_plan( $plan ) ) {
			return $this->normalize_dynamic_post_plan( $plan );
		}

		$plan = wp_parse_args( $plan, $defaults );

		$plan['title']                 = wp_parse_args( is_array( $plan['title'] ) ? $plan['title'] : array(), $defaults['title'] );
		$legacy_title_prompt           = $plan['title']['prompt'] ?? '';
		$plan['title']['value']        = sanitize_text_field( $plan['title']['value'] ?? $legacy_title_prompt );
		$plan['title']['variants']     = $this->sanitize_string_list( $plan['title']['variants'] ?? array() );
		$plan['title']['note']         = sanitize_textarea_field( $plan['title']['note'] ?? $legacy_title_prompt );
		$plan['title']['constraints']  = $this->sanitize_string_list( $plan['title']['constraints'] );
		unset( $plan['title']['prompt'] );

		$plan['content']               = wp_parse_args( is_array( $plan['content'] ) ? $plan['content'] : array(), $defaults['content'] );
		$legacy_global_prompt          = $plan['content']['globalPrompt'] ?? '';
		$plan['content']['globalNote'] = sanitize_textarea_field( $plan['content']['globalNote'] ?? $legacy_global_prompt );
		$plan['content']['keywords']   = $this->sanitize_string_list( $plan['content']['keywords'] );
		$plan['content']['sections']   = $this->sanitize_plan_sections( $plan['content']['sections'] );
		unset( $plan['content']['globalPrompt'] );

		$plan['excerpt']         = wp_parse_args( is_array( $plan['excerpt'] ) ? $plan['excerpt'] : array(), $defaults['excerpt'] );
		$plan['excerpt']['note'] = sanitize_textarea_field( $plan['excerpt']['note'] ?? ( $plan['excerpt']['prompt'] ?? '' ) );
		unset( $plan['excerpt']['prompt'] );

		$plan['seo']                          = wp_parse_args( is_array( $plan['seo'] ) ? $plan['seo'] : array(), $defaults['seo'] );
		$plan['seo']['keywords']             = $this->sanitize_string_list( $plan['seo']['keywords'] );
		$plan['seo']['metaDescriptionPrompt'] = sanitize_textarea_field( $plan['seo']['metaDescriptionPrompt'] );
		$plan['research']                    = $this->sanitize_plan_research( $plan['research'] ?? array(), $defaults['research'] );

		return $plan;
	}

	/**
	 * Normalize dynamic schema and data.
	 *
	 * @param array $plan Dynamic plan.
	 * @return array
	 */
	public function normalize_dynamic_post_plan( $plan ) {
		$schema = $this->normalize_dynamic_plan_schema( $plan['schema'] ?? array() );
		$data   = $this->normalize_dynamic_plan_data( $plan['data'] ?? array(), $schema['fields'] );

		$normalized = array(
			'schema' => $schema,
			'data'   => $data,
			'meta'   => array(
				'format'           => 'dynamic',
				'version'          => max( 1, absint( $plan['meta']['version'] ?? 1 ) ),
				'draftInstruction' => sanitize_textarea_field( $plan['meta']['draftInstruction'] ?? '' ),
			),
		);

		return $normalized;
	}

	/**
	 * Normalize dynamic plan schema.
	 *
	 * @param array $schema Raw schema.
	 * @return array
	 */
	public function normalize_dynamic_plan_schema( $schema ) {
		$schema = is_array( $schema ) ? $schema : array();
		if ( isset( $schema['schema'] ) && is_array( $schema['schema'] ) ) {
			$schema = $schema['schema'];
		}

		$fields = $this->normalize_dynamic_plan_fields( $schema['fields'] ?? array() );

		return array(
			'version' => max( 1, absint( $schema['version'] ?? 1 ) ),
			'fields'  => $fields,
		);
	}

	/**
	 * Normalize schema fields recursively.
	 *
	 * @param array $fields Raw fields.
	 * @return array
	 */
	public function normalize_dynamic_plan_fields( $fields ) {
		if ( ! is_array( $fields ) ) {
			return array();
		}

		$normalized = array();
		$used_keys  = array();

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

			$key = sanitize_key( $field['key'] ?? 'field_' . ( $index + 1 ) );

			if ( '' === $key ) {
				$key = 'field_' . ( $index + 1 );
			}

			$base_key = $key;
			$suffix   = 2;

			while ( isset( $used_keys[ $key ] ) ) {
				$key = $base_key . '_' . $suffix;
				$suffix++;
			}

			$used_keys[ $key ] = true;
			$type              = sanitize_key( $field['type'] ?? 'textarea' );
			$allowed_types     = array( 'text', 'textarea', 'list', 'group', 'repeater' );

			if ( ! in_array( $type, $allowed_types, true ) ) {
				$type = 'textarea';
			}

			$label = sanitize_text_field( $field['label'] ?? '' );
			if ( '' === $label ) {
				$label = ucwords( str_replace( '_', ' ', $key ) );
			}

			$normalized_field = array(
				'key'   => $key,
				'label' => $label,
				'type'  => $type,
			);

			if ( isset( $field['locked'] ) ) {
				$normalized_field['locked'] = ! empty( $field['locked'] );
			}

			if ( isset( $field['required'] ) ) {
				$normalized_field['required'] = ! empty( $field['required'] );
			}

			if ( isset( $field['expert'] ) ) {
				$normalized_field['expert'] = ! empty( $field['expert'] );
			}

			if ( ! empty( $field['help'] ) ) {
				$normalized_field['help'] = sanitize_text_field( $field['help'] );
			} elseif ( ! empty( $field['description'] ) ) {
				$normalized_field['help'] = sanitize_text_field( $field['description'] );
			}

			if ( in_array( $type, array( 'group', 'repeater' ), true ) ) {
				$normalized_field['fields'] = $this->normalize_dynamic_plan_fields( $field['fields'] ?? array() );
			}

			$normalized[] = $normalized_field;
		}

		return $normalized;
	}

	/**
	 * Normalize data recursively against schema fields.
	 *
	 * @param array $data Raw data.
	 * @param array $fields Schema fields.
	 * @return array
	 */
	public function normalize_dynamic_plan_data( $data, $fields ) {
		if ( ! is_array( $data ) ) {
			$data = array();
		}

		$normalized = array();

		foreach ( $fields as $field ) {
			$key = $field['key'];

			$normalized[ $key ] = $this->sanitize_dynamic_plan_value(
				$data[ $key ] ?? null,
				$field
			);
		}

		return $normalized;
	}

	/**
	 * Sanitize a dynamic value based on field type.
	 *
	 * @param mixed $value Raw value.
	 * @param array $field Schema field.
	 * @return mixed
	 */
	public function sanitize_dynamic_plan_value( $value, $field ) {
		$type = $field['type'] ?? 'textarea';

		if ( 'text' === $type ) {
			return sanitize_text_field( is_scalar( $value ) ? (string) $value : '' );
		}

		if ( 'textarea' === $type ) {
			return sanitize_textarea_field( is_scalar( $value ) ? (string) $value : '' );
		}

		if ( 'list' === $type ) {
			return $this->sanitize_string_list( $value );
		}

		if ( 'group' === $type ) {
			return $this->normalize_dynamic_plan_data(
				is_array( $value ) ? $value : array(),
				$field['fields'] ?? array()
			);
		}

		if ( 'repeater' === $type ) {
			$items = array();

			if ( is_array( $value ) ) {
				foreach ( $value as $item ) {
					$items[] = $this->normalize_dynamic_plan_data(
						is_array( $item ) ? $item : array(),
						$field['fields'] ?? array()
					);
				}
			}

			return $items;
		}

		return sanitize_textarea_field( is_scalar( $value ) ? (string) $value : '' );
	}

	/**
	 * Sanitize a list of strings.
	 *
	 * @param mixed $items Raw list.
	 * @return array
	 */
	public function sanitize_string_list( $items ) {
		if ( is_string( $items ) ) {
			$items = preg_split( '/\r\n|\r|\n|,/', $items );
		}

		if ( ! is_array( $items ) ) {
			return array();
		}

		$sanitized = array();

		foreach ( $items as $item ) {
			if ( ! is_scalar( $item ) ) {
				continue;
			}

			$value = sanitize_text_field( (string) $item );

			if ( '' !== $value ) {
				$sanitized[] = $value;
			}
		}

		return array_values( array_unique( $sanitized ) );
	}

	/**
	 * Get default legacy editable plan structure.
	 *
	 * @return array
	 */
	private function get_default_post_plan() {
		return array(
			'title'    => array(
				'value'       => '',
				'variants'    => array(),
				'note'        => '',
				'constraints' => array(),
			),
			'content'  => array(
				'globalNote' => '',
				'keywords'   => array(),
				'sections'   => array(),
			),
			'excerpt'  => array(
				'note' => '',
			),
			'seo'      => array(
				'keywords'              => array(),
				'metaDescriptionPrompt' => '',
			),
			'research' => array(
				'enabled'              => false,
				'searchQueries'        => array(),
				'competitors'          => array(),
				'keywordGaps'          => array(),
				'titlePatterns'        => array(),
				'contentOpportunities' => array(),
			),
		);
	}

	/**
	 * Sanitize content plan sections.
	 *
	 * @param array $sections Raw sections.
	 * @return array
	 */
	private function sanitize_plan_sections( $sections ) {
		$sections = is_array( $sections ) ? $sections : array();
		$clean    = array();

		foreach ( $sections as $section ) {
			$section               = is_array( $section ) ? $section : array();
			$legacy_section_prompt = $section['prompt'] ?? '';
			$clean[]               = array(
				'heading'  => sanitize_text_field( $section['heading'] ?? '' ),
				'intent'   => sanitize_textarea_field( $section['intent'] ?? '' ),
				'note'     => sanitize_textarea_field( $section['note'] ?? $legacy_section_prompt ),
				'keywords' => $this->sanitize_string_list( $section['keywords'] ?? array() ),
			);
		}

		return $clean;
	}

	/**
	 * Sanitize optional competitor research data.
	 *
	 * @param array $research Raw research data.
	 * @param array $defaults Default research structure.
	 * @return array
	 */
	private function sanitize_plan_research( $research, $defaults ) {
		$research = is_array( $research ) ? wp_parse_args( $research, $defaults ) : $defaults;

		$competitors = array();
		foreach ( array_slice( is_array( $research['competitors'] ?? null ) ? $research['competitors'] : array(), 0, 10 ) as $competitor ) {
			$competitor    = is_array( $competitor ) ? $competitor : array();
			$competitors[] = array(
				'title'            => sanitize_text_field( $competitor['title'] ?? '' ),
				'url'              => esc_url_raw( $competitor['url'] ?? '' ),
				'observedKeywords' => array_slice( $this->sanitize_string_list( $competitor['observedKeywords'] ?? array() ), 0, 30 ),
				'headingPatterns'  => array_slice( $this->sanitize_string_list( $competitor['headingPatterns'] ?? array() ), 0, 30 ),
				'contentAngle'     => sanitize_textarea_field( $competitor['contentAngle'] ?? '' ),
			);
		}

		$has_research_data = ! empty( $research['searchQueries'] )
			|| ! empty( $competitors )
			|| ! empty( $research['keywordGaps'] )
			|| ! empty( $research['titlePatterns'] )
			|| ! empty( $research['contentOpportunities'] );

		return array(
			'enabled'              => ! empty( $research['enabled'] ) || $has_research_data,
			'searchQueries'        => array_slice( $this->sanitize_string_list( $research['searchQueries'] ?? array() ), 0, 30 ),
			'competitors'          => $competitors,
			'keywordGaps'          => array_slice( $this->sanitize_string_list( $research['keywordGaps'] ?? array() ), 0, 30 ),
			'titlePatterns'        => array_slice( $this->sanitize_string_list( $research['titlePatterns'] ?? array() ), 0, 30 ),
			'contentOpportunities' => array_slice( $this->sanitize_string_list( $research['contentOpportunities'] ?? array() ), 0, 30 ),
		);
	}
}
