<?php

namespace AIPT\Api\BulkGenerator\Desc\Core;

use AIPT\Utils\PromptFormatter;

class ResponseParser {

	public static function parseUnified( string $response, array $expectedTypes ): ParseResult {
		$result = new ParseResult();

		if ( empty( $response ) ) {
			$result->success = false;
			$result->errors  = array( 'Empty response from AI' );
			return $result;
		}

		$response = trim( $response );

		$parsed = self::tryDirectJsonParse( $response );
		if ( null !== $parsed ) {
			$result->strategy = 'direct_json';
			return self::extractContent( $parsed, $expectedTypes, $result );
		}

		$parsed = self::tryExtractFromCodeBlock( $response );
		if ( null !== $parsed ) {
			$result->strategy = 'code_block_extraction';
			return self::extractContent( $parsed, $expectedTypes, $result );
		}

		$parsed = self::tryExtractJsonObject( $response );
		if ( null !== $parsed ) {
			$result->strategy = 'json_object_extraction';
			return self::extractContent( $parsed, $expectedTypes, $result );
		}

		$parsed = self::tryRegexExtraction( $response, $expectedTypes );
		if ( ! empty( $parsed ) ) {
			$result->strategy = 'regex_extraction';
			return self::extractContent( $parsed, $expectedTypes, $result );
		}

		$result->success = false;
		$result->errors  = array( 'Failed to parse AI response in any format' );
		return $result;
	}

	private static function tryDirectJsonParse( string $response ): ?array {

		if ( strlen( $response ) === 0 || $response[0] !== '{' ) {
			return null;
		}

		$decoded = json_decode( $response, true );
		if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
			return $decoded;
		}

		return null;
	}

	private static function tryExtractFromCodeBlock( string $response ): ?array {
		$cleaned = preg_replace( '/^```json?\s*\n?/i', '', $response );
		$cleaned = preg_replace( '/\n?```\s*$/i', '', $cleaned );
		$cleaned = trim( $cleaned );

		if ( $cleaned === $response ) {
			return null;
		}

		$decoded = json_decode( $cleaned, true );
		if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
			return $decoded;
		}

		return null;
	}

	private static function tryExtractJsonObject( string $response ): ?array {
		if ( preg_match( '/\{.*\}/s', $response, $matches ) ) {
			$decoded = json_decode( $matches[0], true );
			if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
				return $decoded;
			}
		}

		return null;
	}

	private static function tryRegexExtraction( string $response, array $expectedTypes ): array {
		$result = array();

		$keyMappings = array(
			'short_description' => array( 'short_description', 'short', 'summary' ),
			'description'       => array( 'description', 'long_description', 'full_description', 'full' ),
			'tags'              => array( 'tags', 'tag_list', 'keywords' ),
		);

		foreach ( $expectedTypes as $type ) {
			if ( ! isset( $keyMappings[ $type ] ) ) {
				continue;
			}

			foreach ( $keyMappings[ $type ] as $key ) {
				$pattern = '/"' . preg_quote( $key, '/' ) . '"\s*:\s*"((?:[^"\\\\]|\\\\.)*)"/s';
				if ( preg_match( $pattern, $response, $matches ) ) {
					$value = stripcslashes( $matches[1] );
					if ( ! empty( trim( $value ) ) ) {
						$result[ $type ] = $value;
						break;
					}
				}

				$pattern = '/"' . preg_quote( $key, '/' ) . '"\s*:\s*\[(.*?)\]/s';
				if ( preg_match( $pattern, $response, $matches ) ) {
					$arrayContent = $matches[1];
					preg_match_all( '/"([^"]+)"/', $arrayContent, $items );
					if ( ! empty( $items[1] ) ) {
						$result[ $type ] = implode( ', ', $items[1] );
						break;
					}
				}
			}
		}

		return $result;
	}

	private static function extractContent( array $parsed, array $expectedTypes, ParseResult $result ): ParseResult {
		$keyMappings = array(
			'short_description' => array( 'short_description', 'short', 'summary' ),
			'description'       => array( 'description', 'long_description', 'full_description', 'full' ),
			'tags'              => array( 'tags', 'tag_list', 'keywords' ),
		);

		foreach ( $expectedTypes as $type ) {
			$keys  = $keyMappings[ $type ] ?? array( $type );
			$value = self::findValueByKeys( $parsed, $keys );

			if ( null !== $value ) {
				$result->content[ $type ] = self::normalizeContent( $value, $type );
			} else {
				$result->errors[] = "Missing content for: {$type}";
			}
		}

		$result->success = ! empty( $result->content );
		return $result;
	}

	private static function findValueByKeys( array $data, array $keys ): ?string {
		foreach ( $keys as $key ) {
			if ( ! array_key_exists( $key, $data ) ) {
				continue;
			}

			$value = $data[ $key ];

			if ( is_array( $value ) ) {
				$value = implode( ', ', array_map( 'trim', $value ) );
			}

			$value = trim( (string) $value );

			if ( ! empty( $value ) ) {
				return $value;
			}
		}

		return null;
	}

	public static function normalizeContent( string $content, string $type ): string {
		$content = trim( $content );

		if ( $type === 'tags' ) {
			return self::normalizeTags( $content );
		}

		return self::normalizeDescription( $content );
	}

	private static function normalizeDescription( string $content ): string {
		$content = preg_replace( '/^\s*```[a-zA-Z]*\s*\n?/', '', $content );
		$content = preg_replace( '/\n?\s*```\s*$/', '', $content );

		$content = PromptFormatter::formatAiResponseForWordPress( $content );

		return trim( $content );
	}

	private static function normalizeTags( string $content ): string {
		$content = trim( $content );
		$content = trim( $content, '[]' );
		$content = str_replace( array( '"', "'" ), '', $content );
		$content = wp_strip_all_tags( $content );
		$content = str_replace( array( "\r\n", "\r", "\n", ';' ), ',', $content );

		$parts = array_filter( array_map( 'trim', explode( ',', $content ) ) );

		$cleanParts = array();
		foreach ( $parts as $part ) {
			$part = preg_replace( '/[#*_`]/', '', $part );
			$part = preg_replace( '/^tags:\s*/i', '', $part );
			$part = trim( $part );

			if ( ! empty( $part ) && strlen( $part ) <= 100 ) {
				$cleanParts[] = $part;
			}
		}

		return implode( ', ', $cleanParts );
	}

	public static function parseTags( string $rawTags, int $maxCount = 5 ): array {
		$normalized = self::normalizeTags( $rawTags );
		$tags       = array_filter( array_map( 'trim', explode( ',', $normalized ) ) );

		$validTags = array();
		foreach ( $tags as $tag ) {
			if ( ! preg_match( '/[<>{}()\[\]\/\\\\]/', $tag ) && strlen( $tag ) <= 100 ) {
				$validTags[] = $tag;
			}
		}

		return array_slice( $validTags, 0, $maxCount );
	}
}

class ParseResult {
	public bool $success   = false;
	public array $content  = array();
	public array $errors   = array();
	public string $strategy = '';
}
