<?php
/**
 * SEO Analysis response parser.
 *
 * @package SmartyPress_AI_Engine
 */

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

/**
 * Parses and sanitizes Sonar JSON responses.
 */
class SmartyPress_AI_SEO_Analysis_Response_Parser {

	/**
	 * Parse and sanitize the URL discovery response.
	 *
	 * @param string $content AI response.
	 * @param int    $limit   Result limit.
	 * @return array
	 */
	public function parse_competitor_url_response( $content, $limit ) {
		$data = $this->parse_json_object( $content );
		$items = array();

		if ( isset( $data['competitors'] ) && is_array( $data['competitors'] ) ) {
			$items = $data['competitors'];
		} elseif ( is_array( $data ) ) {
			$items = $data;
		}

		$seen_urls = array();
		$competitors = array();

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

			$url = esc_url_raw( $item['url'] ?? '' );
			$title = sanitize_text_field( $item['title'] ?? '' );

			if ( '' === $url || '' === $title ) {
				continue;
			}

			$url_key = strtolower( untrailingslashit( $url ) );
			if ( isset( $seen_urls[ $url_key ] ) ) {
				continue;
			}

			$seen_urls[ $url_key ] = true;
			$competitors[] = array(
				'searchPosition' => absint( $item['searchPosition'] ?? $item['search_position'] ?? 0 ),
				'url'            => $url,
				'title'          => $title,
				'snippet'        => sanitize_textarea_field( $item['snippet'] ?? '' ),
				'pageType'       => sanitize_text_field( $item['pageType'] ?? $item['page_type'] ?? '' ),
				'relevanceScore' => min( 100, absint( $item['relevanceScore'] ?? $item['relevance_score'] ?? 0 ) ),
				'matchReason'    => sanitize_text_field( $item['matchReason'] ?? $item['match_reason'] ?? '' ),
				'analysisStatus' => 'found',
				'headings'       => array(),
				'headingDetails' => array(),
				'keywords'       => array(),
			);

			if ( count( $competitors ) >= $limit ) {
				break;
			}
		}

		return $competitors;
	}

	/**
	 * Parse and sanitize a single page analysis response.
	 *
	 * @param string $content AI response.
	 * @return array
	 */
	public function parse_competitor_page_analysis_response( $content ) {
		$data = $this->parse_json_object( $content );
		$heading_details = array_slice( $this->sanitize_heading_items( $data['headings'] ?? array() ), 0, 20 );
		$headings = array_values(
			array_filter(
				array_map(
					static function( $heading ) {
						return $heading['text'] ?? '';
					},
					$heading_details
				)
			)
		);
		$status = sanitize_key( $data['analysisStatus'] ?? $data['analysis_status'] ?? 'completed' );

		if ( ! in_array( $status, array( 'completed', 'partial', 'failed' ), true ) ) {
			$status = 'completed';
		}

		return array(
			'analysisStatus' => $status,
			'analysisError'  => sanitize_text_field( $data['analysisError'] ?? $data['analysis_error'] ?? '' ),
			'intent'         => sanitize_text_field( $data['intent'] ?? '' ),
			'contentAngle'   => sanitize_text_field( $data['contentAngle'] ?? $data['content_angle'] ?? '' ),
			'matchedTerms'   => array_slice( $this->sanitize_string_list( $data['matchedTerms'] ?? $data['matched_terms'] ?? array() ), 0, 10 ),
			'headings'       => $headings,
			'headingDetails' => $heading_details,
			'keywords'       => array_slice( $this->sanitize_string_list( $data['keywords'] ?? array() ), 0, 12 ),
			'questions'      => array_slice( $this->sanitize_string_list( $data['questions'] ?? array() ), 0, 10 ),
			'contentGaps'    => array_slice( $this->sanitize_string_list( $data['contentGaps'] ?? $data['content_gaps'] ?? array() ), 0, 10 ),
		);
	}

	/**
	 * Sanitize heading objects.
	 *
	 * @param mixed $headings Raw headings.
	 * @return array
	 */
	private function sanitize_heading_items( $headings ) {
		if ( is_string( $headings ) ) {
			$headings = preg_split( '/[\r\n]+/', $headings );
		}

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

		$items = array();
		foreach ( $headings as $heading ) {
			if ( is_array( $heading ) ) {
				$text = sanitize_text_field( $heading['text'] ?? $heading['title'] ?? '' );
				if ( '' === $text ) {
					continue;
				}

				$items[] = array(
					'level'   => sanitize_key( $heading['level'] ?? '' ),
					'text'    => $text,
					'purpose' => sanitize_text_field( $heading['purpose'] ?? '' ),
				);
				continue;
			}

			$text = sanitize_text_field( $heading );
			if ( '' !== $text ) {
				$items[] = array(
					'level'   => '',
					'text'    => $text,
					'purpose' => '',
				);
			}
		}

		return $items;
	}

	/**
	 * Parse first JSON object from response.
	 *
	 * @param string $content AI response.
	 * @return array
	 */
	private function parse_json_object( $content ) {
		$content = trim( (string) $content );
		$candidates = array( $content );

		if ( preg_match( '/```(?:json)?\s*(.*?)\s*```/s', $content, $matches ) ) {
			$candidates[] = trim( $matches[1] );
		}

		$object = $this->extract_first_json_object( $content );
		if ( '' !== $object ) {
			$candidates[] = $object;
		}

		foreach ( array_unique( array_filter( $candidates ) ) as $candidate ) {
			$data = json_decode( $candidate, true );
			if ( JSON_ERROR_NONE === json_last_error() && is_array( $data ) ) {
				return $data;
			}
		}

		return array();
	}

	/**
	 * Extract first balanced JSON object.
	 *
	 * @param string $content AI response.
	 * @return string
	 */
	private function extract_first_json_object( $content ) {
		$start = strpos( $content, '{' );
		if ( false === $start ) {
			return '';
		}

		$length = strlen( $content );
		$depth = 0;
		$in_string = false;
		$escaped = false;

		for ( $index = $start; $index < $length; $index++ ) {
			$char = $content[ $index ];

			if ( $in_string ) {
				if ( $escaped ) {
					$escaped = false;
				} elseif ( '\\' === $char ) {
					$escaped = true;
				} elseif ( '"' === $char ) {
					$in_string = false;
				}
				continue;
			}

			if ( '"' === $char ) {
				$in_string = true;
			} elseif ( '{' === $char ) {
				$depth++;
			} elseif ( '}' === $char ) {
				$depth--;
				if ( 0 === $depth ) {
					return substr( $content, $start, $index - $start + 1 );
				}
			}
		}

		return '';
	}

	/**
	 * Sanitize a scalar or array list.
	 *
	 * @param mixed $values Raw values.
	 * @return array
	 */
	private function sanitize_string_list( $values ) {
		if ( is_string( $values ) ) {
			$values = preg_split( '/[\r\n,]+/', $values );
		}

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

		return array_values(
			array_filter(
				array_map(
					static function( $value ) {
						return sanitize_text_field( $value );
					},
					$values
				)
			)
		);
	}
}
