<?php
/**
 * SEO metadata preparation for generated posts.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Builds a normalized SEO package from generated post fields.
 */
class SmartyPress_AI_Content_Generator_SEO_Generation_Metadata_Service {

	const MIN_CONTENT_WORDS = 600;
	const MAX_SLUG_WORDS    = 5;

	/**
	 * Normalize post data before it is saved.
	 *
	 * @param array $post_data Generated post data.
	 * @return array
	 */
	public function normalize_post_data( $post_data ) {
		$post_data = is_array( $post_data ) ? $post_data : array();

		if ( ! empty( $post_data['post_content_blocks'] ) && is_array( $post_data['post_content_blocks'] ) ) {
			$post_data['post_content_blocks'] = $this->remove_cta_blocks( $post_data['post_content_blocks'] );
		}

		$metadata = $this->build_package( $post_data );
		if ( ! empty( $post_data['seo_metadata'] ) && is_array( $post_data['seo_metadata'] ) ) {
			$metadata = $this->merge_packages( $metadata, $post_data['seo_metadata'] );
		}

		$metadata = $this->enforce_primary_keyword_package( $metadata, $post_data );

		if ( empty( $post_data['post_excerpt'] ) && ! empty( $metadata['meta_description'] ) ) {
			$post_data['post_excerpt'] = $metadata['meta_description'];
		}

		$post_data['seo_metadata'] = $metadata;

		return $post_data;
	}

	/**
	 * Build normalized metadata package.
	 *
	 * @param array $post_data Generated post data.
	 * @return array
	 */
	public function build_package( $post_data ) {
		$title    = sanitize_text_field( $post_data['post_title'] ?? $post_data['title'] ?? '' );
		$excerpt  = sanitize_textarea_field( $post_data['post_excerpt'] ?? $post_data['excerpt'] ?? '' );
		$content  = $this->extract_content_text( $post_data );
		$keywords = $this->collect_keywords( $post_data );
		$primary  = $keywords[0] ?? $this->derive_primary_keyword( $title, $content );

		if ( '' !== $primary && empty( $keywords ) ) {
			$keywords[] = $primary;
		}

		$seo_title        = $this->build_seo_title( $title, $primary );
		$meta_description = $this->build_meta_description( $excerpt, $content, $primary );
		$slug             = $this->build_slug( $primary, $title );

		return array(
			'seo_title'        => $seo_title,
			'meta_description' => $meta_description,
			'slug'             => $slug,
			'focus_keywords'   => $keywords,
			'primary_keyword'  => $primary,
			'secondary_keywords' => array_values( array_slice( $keywords, 1, 4 ) ),
			'word_count'       => $this->count_words( $content ),
		);
	}

	/**
	 * Merge generated and caller-provided metadata packages.
	 *
	 * @param array $base Base package.
	 * @param array $override Override package.
	 * @return array
	 */
	private function merge_packages( $base, $override ) {
		$override = is_array( $override ) ? $override : array();
		$merged   = $base;

		foreach ( array( 'seo_title', 'meta_description', 'slug', 'primary_keyword' ) as $key ) {
			if ( ! empty( $override[ $key ] ) ) {
				$merged[ $key ] = sanitize_text_field( $override[ $key ] );
			}
		}

		if ( ! empty( $override['focus_keywords'] ) ) {
			$merged['focus_keywords'] = $this->sanitize_keywords( $override['focus_keywords'] );
		}

		if ( empty( $merged['primary_keyword'] ) && ! empty( $merged['focus_keywords'][0] ) ) {
			$merged['primary_keyword'] = $merged['focus_keywords'][0];
		}

		$merged['secondary_keywords'] = array_values( array_slice( (array) ( $merged['focus_keywords'] ?? array() ), 1, 4 ) );

		return $this->enforce_primary_keyword_package( $merged );
	}

	/**
	 * Ensure Rank Math can find the primary keyword in key SEO fields.
	 *
	 * @param array $metadata Metadata package.
	 * @param array $post_data Optional post data.
	 * @return array
	 */
	private function enforce_primary_keyword_package( $metadata, $post_data = array() ) {
		$metadata = is_array( $metadata ) ? $metadata : array();
		$keywords = $this->sanitize_keywords( $metadata['focus_keywords'] ?? array() );
		$primary  = sanitize_text_field( $metadata['primary_keyword'] ?? ( $keywords[0] ?? '' ) );

		if ( '' !== $primary ) {
			$keywords = array_values( array_unique( array_filter( array_merge( array( $primary ), $keywords ) ) ) );
		}

		if ( empty( $keywords ) ) {
			return $metadata;
		}

		$primary = $keywords[0];
		$title   = sanitize_text_field( $metadata['seo_title'] ?? ( $post_data['post_title'] ?? '' ) );
		if ( '' === $title ) {
			$title = $primary;
		}

		$description = sanitize_textarea_field( $metadata['meta_description'] ?? ( $post_data['post_excerpt'] ?? '' ) );
		if ( '' === $description ) {
			$description = $this->build_meta_description(
				$post_data['post_excerpt'] ?? '',
				$this->extract_content_text( $post_data ),
				$primary
			);
		}

		$primary_slug = sanitize_title( $primary );
		$slug         = sanitize_title( $metadata['slug'] ?? '' );
		if ( '' === $slug && '' !== $primary_slug ) {
			$slug = $primary_slug;
		}

		$metadata['focus_keywords']     = array_values( array_slice( $keywords, 0, 5 ) );
		$metadata['primary_keyword']    = $primary;
		$metadata['secondary_keywords'] = array_values( array_slice( $metadata['focus_keywords'], 1, 4 ) );
		$metadata['seo_title']          = wp_trim_words( $title, 10, '' );
		$metadata['meta_description']   = $this->trim_meta_description( $description, $primary );
		$metadata['slug']               = $slug;

		return $metadata;
	}

	/**
	 * Remove generated CTA blocks from structured article content.
	 *
	 * @param array $article Structured article blocks.
	 * @return array
	 */
	private function remove_cta_blocks( $article ) {
		if ( empty( $article['content']['blocks'] ) || ! is_array( $article['content']['blocks'] ) ) {
			return $article;
		}

		$article['content']['blocks'] = array_values(
			array_filter(
				$article['content']['blocks'],
				function ( $block ) {
					return ! is_array( $block ) || 'cta' !== sanitize_key( $block['type'] ?? '' );
				}
			)
		);

		return $article;
	}

	/**
	 * Collect keywords from generated fields.
	 *
	 * @param array $post_data Generated post data.
	 * @return array
	 */
	private function collect_keywords( $post_data ) {
		$keywords = array();

		foreach ( array( 'seo_keywords', 'focus_keywords', 'keywords' ) as $key ) {
			if ( ! empty( $post_data[ $key ] ) ) {
				$keywords = array_merge( $keywords, $this->sanitize_keywords( $post_data[ $key ] ) );
			}
		}

		if ( ! empty( $post_data['seo_metadata']['focus_keywords'] ) ) {
			$keywords = array_merge( $keywords, $this->sanitize_keywords( $post_data['seo_metadata']['focus_keywords'] ) );
		}

		if ( ! empty( $post_data['post_content_blocks']['seo_keywords'] ) ) {
			$keywords = array_merge( $keywords, $this->sanitize_keywords( $post_data['post_content_blocks']['seo_keywords'] ) );
		}

		return array_values( array_slice( array_unique( array_filter( $keywords ) ), 0, 5 ) );
	}

	/**
	 * Sanitize keyword list.
	 *
	 * @param string|array $keywords Keywords.
	 * @return array
	 */
	private function sanitize_keywords( $keywords ) {
		if ( is_string( $keywords ) ) {
			$keywords = preg_split( '/\r\n|\r|\n|,/', $keywords );
		}

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

		$items = array();
		foreach ( $keywords as $keyword ) {
			if ( ! is_scalar( $keyword ) ) {
				continue;
			}

			$value = trim( preg_replace( '/\s+/u', ' ', sanitize_text_field( (string) $keyword ) ) );
			if ( '' !== $value ) {
				$items[] = $value;
			}
		}

		return array_values( array_unique( $items ) );
	}

	/**
	 * Extract readable content text from generated post data.
	 *
	 * @param array $post_data Generated post data.
	 * @return string
	 */
	private function extract_content_text( $post_data ) {
		$parts = array();

		if ( ! empty( $post_data['post_content'] ) ) {
			$parts[] = wp_strip_all_tags( (string) $post_data['post_content'] );
		}

		$article = $post_data['post_content_blocks'] ?? array();
		if ( is_array( $article ) ) {
			if ( ! empty( $article['content']['lead'] ) ) {
				$parts[] = wp_strip_all_tags( (string) $article['content']['lead'] );
			}

			foreach ( (array) ( $article['content']['blocks'] ?? array() ) as $block ) {
				if ( ! is_array( $block ) ) {
					continue;
				}

				foreach ( array( 'text', 'content', 'title' ) as $key ) {
					if ( ! empty( $block[ $key ] ) && is_scalar( $block[ $key ] ) ) {
						$parts[] = wp_strip_all_tags( (string) $block[ $key ] );
					}
				}

				foreach ( (array) ( $block['items'] ?? array() ) as $item ) {
					if ( is_scalar( $item ) ) {
						$parts[] = wp_strip_all_tags( (string) $item );
					}
				}
			}
		}

		return trim( preg_replace( '/\s+/u', ' ', implode( ' ', $parts ) ) );
	}

	/**
	 * Build SEO title.
	 *
	 * @param string $title Title.
	 * @param string $primary Primary keyword.
	 * @return string
	 */
	private function build_seo_title( $title, $primary ) {
		$title   = sanitize_text_field( $title );
		$primary = sanitize_text_field( $primary );

		if ( '' === $title ) {
			$title = $primary;
		}

		return wp_trim_words( $title, 10, '' );
	}

	/**
	 * Build meta description.
	 *
	 * @param string $excerpt Excerpt.
	 * @param string $content Content text.
	 * @param string $primary Primary keyword.
	 * @return string
	 */
	private function build_meta_description( $excerpt, $content, $primary ) {
		$description = '' !== trim( $excerpt ) ? $excerpt : wp_trim_words( $content, 24, '' );
		$description = sanitize_textarea_field( $description );
		$primary     = sanitize_text_field( $primary );

		if ( '' === $description ) {
			$description = $primary;
		}

		return $this->trim_meta_description( $description, $primary );
	}

	/**
	 * Build concise slug.
	 *
	 * @param string $primary Primary keyword.
	 * @param string $title Title.
	 * @return string
	 */
	private function build_slug( $primary, $title ) {
		$source = '' !== trim( $primary ) ? $primary : $title;
		$words  = preg_split( '/\s+/u', wp_strip_all_tags( (string) $source ), -1, PREG_SPLIT_NO_EMPTY );

		if ( is_array( $words ) && count( $words ) > self::MAX_SLUG_WORDS ) {
			$source = implode( ' ', array_slice( $words, 0, self::MAX_SLUG_WORDS ) );
		}

		return sanitize_title( $source );
	}

	/**
	 * Derive a fallback primary keyword from title/content.
	 *
	 * @param string $title Title.
	 * @param string $content Content.
	 * @return string
	 */
	private function derive_primary_keyword( $title, $content ) {
		$text  = trim( $title ) ?: $content;
		$words = preg_split( '/\s+/u', wp_strip_all_tags( (string) $text ), -1, PREG_SPLIT_NO_EMPTY );

		if ( ! is_array( $words ) || empty( $words ) ) {
			return '';
		}

		return sanitize_text_field( implode( ' ', array_slice( $words, 0, min( 3, count( $words ) ) ) ) );
	}

	/**
	 * Count content words.
	 *
	 * @param string $text Text.
	 * @return int
	 */
	private function count_words( $text ) {
		$words = preg_split( '/\s+/u', wp_strip_all_tags( (string) $text ), -1, PREG_SPLIT_NO_EMPTY );

		return is_array( $words ) ? count( $words ) : 0;
	}

	/**
	 * Trim meta description while preserving the primary keyword.
	 *
	 * @param string $description Description.
	 * @param string $primary Primary keyword.
	 * @return string
	 */
	private function trim_meta_description( $description, $primary ) {
		$description = sanitize_textarea_field( $description );
		$primary     = sanitize_text_field( $primary );

		if ( function_exists( 'mb_strlen' ) && function_exists( 'mb_substr' ) ) {
			if ( mb_strlen( $description ) <= 155 ) {
				return $description;
			}

			if ( '' !== $primary && 0 === $this->stripos( $description, $primary ) ) {
				$tail = ltrim( trim( mb_substr( $description, mb_strlen( $primary ), 155 - mb_strlen( $primary ) ) ), " \t\n\r\0\x0B:;-," );
				return rtrim( $primary . ' ' . $tail, " \t\n\r\0\x0B.,;:-" );
			}

			return rtrim( mb_substr( $description, 0, 155 ), " \t\n\r\0\x0B.,;:-" );
		}

		return strlen( $description ) > 155 ? rtrim( substr( $description, 0, 155 ), " \t\n\r\0\x0B.,;:-" ) : $description;
	}

	/**
	 * Case-insensitive substring search with mbstring fallback.
	 *
	 * @param string $haystack Haystack.
	 * @param string $needle Needle.
	 * @return int|false
	 */
	private function stripos( $haystack, $needle ) {
		if ( function_exists( 'mb_stripos' ) ) {
			return mb_stripos( $haystack, $needle );
		}

		return stripos( $haystack, $needle );
	}
}
