<?php
namespace Actirise\Includes;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

use WP_Error;
use WP_Filesystem_Base;
use DateTime;
use DateTimeZone;
use Actirise\Includes\Api;
use Actirise\Includes\Options;

/**
 * Helpers class.
 *
 * @link       https://actirise.com
 * @since      2.0.0
 * @package    actirise
 * @subpackage actirise/includes
 * @author     actirise <wordpress@actirise.com>
 */
class Helpers {
	/**
	 * Get page type.
	 *
	 * @since 2.0.0
	 * @return string
	 */
	public static function get_page_type() {
		$page_type = 'notfound';

		if ( is_home() || is_front_page() ) {
			$page_type = 'home';
		} elseif ( is_page() ) {
			$page_type = 'page';
		} elseif ( is_single() ) {
			$page_type = 'article';
		} elseif ( is_category() ) {
			$page_type = 'category';
		} elseif ( is_tag() ) {
			$page_type = 'tag';
		} elseif ( is_tax() ) {
			$page_type = 'tax';
		} elseif ( is_archive() ) {
			$page_type = 'archive';
		} elseif ( is_search() ) {
			$page_type = 'search';
		} elseif ( is_404() ) {
			$page_type = 'notfound';
		}

		return $page_type;
	}

	/**
	 * Get page url.
	 *
	 * @since 2.0.0
	 * @return string
	 */
	public static function get_url() {
		return esc_url_raw( self::get_server_details()['host'] . self::get_server_details()['uri'] );
	}

	/**
	 * Get the full URL of the Actirise plugin page in the WP admin dashboard.
	 *
	 * @since 4.2.0
	 * @return string
	 */
	public static function get_page_url() {
		return add_query_arg( array( 'page' => 'actirise-settings' ), admin_url( 'admin.php' ) );
	}

	/**
	 * Get all options for FastCmp
	 *
	 * @since 2.2.0
	 * @param bool $light If true, return only the light options.
	 * @return array{name: string, enabled: boolean, headOffice: string, logo: string, privacyPolicy: string, targetedAudience: string, excludedIabVendors: array<int>, excludedGoogleVendors: array<int>, stubLight: string, vendors: array<mixed>, uuid: string, customsVendor: array{name: string, id: string}, acceptButtonStyle: array{bg: string, font: string}, declineButtonStyle: array{bg: string, font: string, border: string}, parametersButtonStyle: array{bg: string, font: string, border: string}, customStyle: string, consentV2: string, googleTagsStatus: string, googleAdStorage: string, googleAnalyticsStorage: string, googlePersonalizationStorage: string, googleFunctionalityStorage: string, googleSecurityStorage: string, googleAdPersonalization: string, googleAdUserData: string, googleSpecificEnabled: string, googleSpecificData: string}
	 */
	public static function get_fastcmp_options( $light ) {
		$organization_country = Options::get( 'organization-country', '' );

		/** @var string $image */
		$image = Options::get( 'fastcmp-logo', '' );

		if ( $image === '' ) {
				/** @var int|boolean $logo_id */
			$logo_id = get_theme_mod( 'custom_logo' );

			if ( $logo_id ) {
				/** @var int $logo_id */
				$image_attachment = wp_get_attachment_image_src( $logo_id, 'full' );

				if ( $image_attachment !== false && count( $image_attachment ) > 0 ) {
					/** @var string $image */
					$image = $image_attachment[0];
				}
			}
		}

		$privacy_policy_option = Options::get( 'fastcmp-privacypolicy', '' );
		if ( function_exists( 'get_privacy_policy_url' ) ) {
			/** @var string $privacy_policy */
			$privacy_policy = get_privacy_policy_url();
		} else {
			$privacy_policy = '';
		}

		/** @var string $privacy_policy */
		$privacy_policy = $privacy_policy_option === '' ? $privacy_policy : $privacy_policy_option;
		/** @var string $name */
		$name = Options::get( 'fastcmp-name', get_bloginfo( 'name', 'raw' ) );
		/** @var boolean $enabled */
		$enabled = Options::get( 'fastcmp-enabled', null );
		/** @var string $head_office */
		$head_office = Options::get( 'fastcmp-headoffice', $organization_country );
		/** @var array<int> $excluded_iab_vendors */
		$excluded_iab_vendors = Options::get( 'fastcmp-excludediabvendors', array() );
		/** @var array<int> $excluded_google_vendors */
		$excluded_google_vendors = Options::get( 'fastcmp-excludedgooglevendors', array() );
		/** @var string $stub_light */
		$stub_light = Options::get( 'fastcmp-stub-light', '' );
		/** @var string $targeted_audience */
		$targeted_audience = Options::get( 'fastcmp-targetedaudience', 'tcfeuv2' );
		/** @var array<mixed> $vendors */
		$vendors = $light ? array() : Options::get( 'fastcmp-vendors', array() );
		/** @var string $uuid */
		$uuid = Options::get( 'fastcmp-uuid', '' );
		/** @var array{name: string, id: string} $customs_vendor */
		$customs_vendor = Options::get( 'fastcmp-customsvendor', array() );
		/** @var array{bg: string, font: string} $accept_button_style */
		$accept_button_style = Options::get(
			'fastcmp-acceptButtonStyle',
			array(
				'bg'   => 'transparent',
				'font' => '#6f6f6f',
			)
		);
		/** @var string $accept_button_style_string */
		$accept_button_style_string = wp_json_encode( $accept_button_style );
		/** @var array{bg: string, font: string} $accept_button_style */
		$accept_button_style = json_decode( $accept_button_style_string, true );

		/** @var array{bg: string, font: string, border: string} $decline_button_style */
		$decline_button_style = Options::get(
			'fastcmp-declineButtonStyle',
			array(
				'bg'     => 'transparent',
				'font'   => '#6f6f6f',
				'border' => '#6f6f6f',
			)
		);
		/** @var string $decline_button_style_string */
		$decline_button_style_string = wp_json_encode( $decline_button_style );
		/** @var array{bg: string, font: string, border: string} $decline_button_style */
		$decline_button_style = json_decode( $decline_button_style_string, true );

		/** @var array{bg: string, font: string, border: string} $parameters_button_style */
		$parameters_button_style = Options::get(
			'fastcmp-parametersButtonStyle',
			array(
				'bg'     => 'transparent',
				'font'   => '#6f6f6f',
				'border' => '#6f6f6f',
			)
		);
		/** @var string $parameters_button_style_string */
		$parameters_button_style_string = wp_json_encode( $parameters_button_style );
		/** @var array{bg: string, font: string, border: string} $parameters_button_style */
		$parameters_button_style = json_decode( $parameters_button_style_string, true );

		/** @var string $custom_style */
		$custom_style = Options::get( 'fastcmp-customStyle', '' );

		/** @var string $lastupdate */
		$lastupdate = Options::get( 'fastcmp-lastupdate', '' );

		/** @var string $consent_v2 */
		$consent_v2 = Options::get( 'fastcmp-consentV2', false );
		/** @var string $google_tags_status */
		$google_tags_status = Options::get( 'fastcmp-googleTagsStatus', false );
		/** @var string $google_ad_storage */
		$google_ad_storage = Options::get( 'fastcmp-googleAdStorage', false );
		/** @var string $google_analytics_storage */
		$google_analytics_storage = Options::get( 'fastcmp-googleAnalyticsStorage', false );
		/** @var string $google_personalization_storage */
		$google_personalization_storage = Options::get( 'fastcmp-googlePersonalizationStorage', false );
		/** @var string $google_functionality_storage */
		$google_functionality_storage = Options::get( 'fastcmp-googleFunctionalityStorage', false );
		/** @var string $google_security_storage */
		$google_security_storage = Options::get( 'fastcmp-googleSecurityStorage', false );
		/** @var string $google_ad_personalization */
		$google_ad_personalization = Options::get( 'fastcmp-googleAdPersonalization', false );
		/** @var string $google_ad_user_data */
		$google_ad_user_data = Options::get( 'fastcmp-googleAdUserData', false );
		/** @var string $google_specific_enabled */
		$google_specific_enabled = Options::get( 'fastcmp-googleSpecificEnabled', false );
		/** @var string $google_specific_data */
		$google_specific_data = Options::get( 'fastcmp-googleSpecificData', '' );

		return array(
			'name'                         => $name,
			'enabled'                      => $enabled,
			'headOffice'                   => $head_office,
			'logo'                         => $image,
			'privacyPolicy'                => $privacy_policy,
			'targetedAudience'             => $targeted_audience,
			'excludedIabVendors'           => $excluded_iab_vendors,
			'excludedGoogleVendors'        => $excluded_google_vendors,
			'customsVendor'                => $customs_vendor,
			'stubLight'                    => $stub_light,
			'vendors'                      => $vendors,
			'uuid'                         => $uuid,
			'acceptButtonStyle'            => $accept_button_style,
			'declineButtonStyle'           => $decline_button_style,
			'parametersButtonStyle'        => $parameters_button_style,
			'customStyle'                  => $custom_style,
			'lastUpdate'                   => $lastupdate,
			'consentV2'                    => $consent_v2,
			'googleTagsStatus'             => $google_tags_status,
			'googleAdStorage'              => $google_ad_storage,
			'googleAnalyticsStorage'       => $google_analytics_storage,
			'googlePersonalizationStorage' => $google_personalization_storage,
			'googleFunctionalityStorage'   => $google_functionality_storage,
			'googleSecurityStorage'        => $google_security_storage,
			'googleAdPersonalization'      => $google_ad_personalization,
			'googleAdUserData'             => $google_ad_user_data,
			'googleSpecificEnabled'        => $google_specific_enabled,
			'googleSpecificData'           => $google_specific_data,
		);
	}

	/**
	 * Get server details.
	 *
	 * @since 2.3.0
	 * @return array<string>
	 */
	public static function get_server_details() {
		$data = array();

		$data['host']   = null;
		$data['scheme'] = isset( $_SERVER['REQUEST_SCHEME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_SCHEME'] ) ) : '';
		$data['uri']    = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$data['method'] = isset( $_SERVER['REQUEST_METHOD'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) : '';

		$website_domain = wp_parse_url( get_bloginfo( 'url' ) );

		if ( is_array( $website_domain ) && isset( $website_domain['host'] ) ) {
			$data['host'] = $website_domain['host'];
		}

		if ( empty( $data['host'] ) ) {
			$data['host'] = isset( $_SERVER['HTTP_HOST'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) : '';
		}

		if ( ! empty( $data['host'] ) ) {
			/** @var string $host */
			$host         = preg_replace( '/^w{3,}\.(.*)$/', '$1', strtolower( $data['host'] ) );
			$data['host'] = $host;
		}

		return $data;
	}

	/**
	 * Check if plugin auto update is active.
	 *
	 * @since    2.4.0
	 * @return bool
	 */
	public static function has_auto_update() {
		/** @var string $plugin_file */
		$plugin_file = plugin_basename( ACTIRISE_FILE );
		/** @var array<string> $auto_updates */
		$auto_updates = get_site_option( 'auto_update_plugins', array() );
		/** @var bool $auto_updates_enabled */
		$auto_updates_enabled = in_array( $plugin_file, $auto_updates, true );

		return $auto_updates_enabled;
	}

	/**
	 * Get all custom value.
	 *
	 * @since    2.0.0
	 * @param    string $type_custom The type of custom.
	 * @param    string $custom_name The name of custom.
	 * @return   string
	 */
	public static function get_custom_value( $type_custom, $custom_name ) {
		$value = '';

		if ( is_home() || is_front_page() ) {
			$value = self::apply_custom_value_filter( $custom_name, $value );

			return $value;
		}

		if ( $type_custom !== '' ) {
			$custom_exploded = explode( '_', $type_custom, 2 );
			$type_custom_key = $custom_exploded[0];
			$level_custom    = '0';
			$key             = $custom_exploded[1];

			if ( $type_custom_key === 'category' || $type_custom_key === 'tag' ) {
				$custom_exploded = explode( '_', $type_custom, 3 );
				$level_custom    = $custom_exploded[1];
				$key             = $custom_exploded[2];
			}

			$post_id    = get_the_ID();
			$post_value = get_post( ! $post_id ? null : $post_id );

			if ( ! $post_value || ! $post_id ) {
				$value = '';
			} else {
				switch ( $type_custom_key ) {
					case 'post':
						if ( is_single() || is_page() ) {
							if ( $post_value->$key ) {
								/** @var string $value */
								$value = $post_value->$key;
							}
						}
						break;
					case 'category':
						// Check if we're on a category archive page
						if ( is_category() ) {
							$queried_object = get_queried_object();
							if ( $queried_object instanceof \WP_Term ) {
								/** @var string $value */
								$value = $queried_object->$key;
							}
						} else {
							// Original logic for single post/page with categories
							$category = get_the_category();

							if ( $category && isset( $category[ $level_custom ] ) ) {
								/** @var string $value */
								$value = $category[ $level_custom ]->$key;
							}
						}
						break;
					case 'tag':
						// Check if we're on a tag archive page
						if ( is_tag() ) {
							$queried_object = get_queried_object();
							if ( $queried_object instanceof \WP_Term ) {
								/** @var string $value */
								$value = $queried_object->$key;
							}
						} else {
							// Original logic for single post/page with tags
							$tags = get_the_tags( $post_id );

							if ( $tags && ! is_wp_error( $tags ) && isset( $tags[ $level_custom ] ) ) {
								/** @var string $value */
								$value = $tags[ $level_custom ]->$key;
							}
						}

						break;
					case 'author':
						if ( is_single() || is_page() ) {
							/** @var int $author_id */
							$author_id = $post_value->post_author;
							/** @var string $value */
							$value = get_the_author_meta( 'display_name', $author_id );

							if ( $value === '' ) {
								/** @var string $value */
								$value = get_the_author_meta( $key, $author_id );
							}
						}
						break;
					case 'customFields':
						/** @var string $value */
						$value = get_post_meta( $post_id, $key, true );

						break;
					default:
						$value = '';
						break;
				}
			}
		}

		$value = self::apply_custom_value_filter( $custom_name, $value );

		/** @var string $value */
		return $value;
	}

	/**
	 * Get WordPress filesystem.
	 *
	 * @since 2.5.0
	 * @return WP_Filesystem_Base|WP_Error
	 */
	public static function get_wp_fs() {
		global $wp_filesystem;

		if ( ! $wp_filesystem instanceof WP_Filesystem_Base ) {
			$initialized = self::init_wp_fs();

			if ( false === $initialized ) {
				return new WP_Error( 'WP_FS_HELPER', 'The WordPress filesystem could not be initialized.' );
			}
		}

		/** @var WP_Filesystem_Base $wp_filesystem */
		return $wp_filesystem;
	}

	/**
	 * Init WP_Filesystem.
	 *
	 * @since 2.5.0
	 * @return bool
	 */
	protected static function init_wp_fs() {
		global $wp_filesystem;

		if ( $wp_filesystem instanceof WP_Filesystem_Base ) {
			return true;
		}

		require_once ABSPATH . 'wp-admin/includes/file.php';

		$method      = get_filesystem_method();
		$initialized = false;

		if ( 'direct' === $method ) {
			$initialized = WP_Filesystem();
		} elseif ( false !== $method ) {
			// See https://core.trac.wordpress.org/changeset/56341.
			ob_start();
			$credentials = request_filesystem_credentials( '' );
			ob_end_clean();

			$initialized = $credentials && WP_Filesystem( $credentials );
		}

		return is_null( $initialized ) ? false : $initialized;
	}

	/**
	 * Generate a random token in uuid v4 format
	 *
	 * @since 2.5.5
	 * @param  string $prefix
	 * @return string
	 */
	public static function generate_token( $prefix = '' ) {
		$uid = uniqid( '', true );
		$uid = str_replace( '.', '', $uid );

		$uid = str_shuffle( $uid );

		$uuid = sprintf(
			'%05s-%05s-%05d-%05d',
			substr( $uid, 0, 5 ),
			substr( $uid, 5, 5 ),
			( hexdec( substr( $uid, 10, 5 ) ) & 0x0fff ) | 0x5000,
			( hexdec( substr( $uid, 15, 5 ) ) & 0x3fff ) | 0x5000
		);

		return $prefix . $uuid;
	}

	/**
	 * Hash token for API request
	 *
	 * @since 2.5.5
	 * @param string $token
	 * @return string
	 */
	public static function hash_token( $token = '' ) {
		/** @var string $domain */
		$domain = self::get_server_details()['host'];
		/** @var string $datetime */
		$datetime = ( new DateTime( 'now', new DateTimeZone( 'UTC' ) ) )->format( 'Y-m-d' );
		/** @var string $concatenated_string */
		$concatenated_string = $token . $domain . $datetime;

		return md5( $concatenated_string );
	}

	/**
	 * Get website informations
	 *
	 * @since 3.0.0
	 * @return array<string, mixed>
	 */
	public static function get_website_info() {
		/** @var array<string, mixed> $website */
		$website = array();
		/** @var string $token */
		$token = Options::get( 'settings-analytics-token', '' );

		if ( $token !== '' ) {
			$api      = new Api();
			$args     = array(
				'exact[domain]'  => rawurlencode( self::get_server_details()['host'] ),
				'websiteProduct' => '/v2.0/website_products/3',
			);
			$response = $api->get( 'api', 'websites', $args, $token, true );

			if ( is_wp_error( $response ) || ! is_array( $response ) || ! is_array( $response['hydra:member'] ) ) {
				Logger::add_log( 'get_website_info error', 'include/helpers', 'error' );

				return $website;
			}

			if ( empty( $response['hydra:member'] ) ) {
				return $website;
			}

			/** @var array<string, mixed> $website */
			$website = array_shift( $response['hydra:member'] );

			if ( ! is_array( $website ) ) {
				/** @var array<string, mixed> $website */
				$website = array();
			}
		}

		return $website;
	}

	/**
	 * Send error
	 *
	 * @since 3.0.0
	 * @param string $code
	 * @param string $message
	 * @return void
	 */
	public static function send_error( $code, $message ) {
		wp_send_json_error( new WP_Error( $code, $message ) );
	}

	/**
	 * Check if JS custom code for cmp consent is valid
	 *
	 * @since 2.6.9
	 * @param string $code
	 * @return bool|WP_Error
	 */
	public static function cmp_consent_valid_code( $code ) {
		$code = trim( $code );

		if ( empty( $code ) ) {
			return false;
		}

		$requiredKeys = array(
			'region',
			'ad_storage',
			'analytics_storage',
			'personalization_storage',
			'functionality_storage',
			'security_storage',
			'ad_personalization',
			'ad_user_data',
		);

		$allowedValues = array( 'granted', 'denied' );

		/** @var string $code */
		$code = '[' . preg_replace( '/}\s*,\s*{/', '},{', $code ) . ']';
		/** @var string $code */
		$code = preg_replace_callback(
			'/([a-zA-Z_]+)\s*:/',
			function ( $matches ) {
				return '"' . $matches[1] . '":';
			},
			$code
		);

		/** @var string $code */
		$code = preg_replace( "/'([^']*)'/", '"$1"', $code );

		/** @var string $code */
		$code = preg_replace_callback(
			'/"region"\s*:\s*\[(.*?)\]/',
			function ( $matches ) {
				$regions = array_map( 'trim', explode( ',', $matches[1] ) );
				$regions = array_map(
					function ( $item ) {
						return '"' . trim( $item, '"\' ' ) . '"';
					},
					$regions
				);
				return '"region":[' . implode( ',', $regions ) . ']';
			},
			$code
		);

		/** @var string $code */
		$code = preg_replace( '/\s+/', '', $code );
		/** @var string $code */
		$code = preg_replace( '/,}/', '}', $code );

		/** @var string $code */
		$data = json_decode( $code, true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return new WP_Error( 'cmp_consent_invalid_code', 'Invalid JSON format.' );
		}

		if ( ! is_array( $data ) || empty( $data ) ) {
			return new WP_Error( 'cmp_consent_invalid_code', 'Invalid JSON format.' );
		}

		foreach ( $data as $object ) {
			$foundKey = 0;
			/** @var array<string> $object */
			foreach ( $object as $key => $value ) {
				if ( ! in_array( $key, $requiredKeys, true ) ) {
					continue;
				}

				++$foundKey;

				if ( $key === 'region' ) {
					if ( ! is_array( $value ) || empty( $value ) ) {
						return new WP_Error( 'cmp_consent_invalid_code', 'Invalid region format.' );
					}
					continue;
				}

				if ( ! in_array( $value, $allowedValues, true ) ) {
					return new WP_Error( 'cmp_consent_invalid_code', 'Invalid value for key: ' . $key );
				}
			}

			if ( $foundKey !== count( $requiredKeys ) ) {
				return new WP_Error( 'cmp_consent_invalid_code', 'Missing required keys.' );
			}
		}

		return false;
	}

	/**
	 * Get user role from API.
	 *
	 * @since 3.0.0
	 * @param string $user_id User ID.
	 * @param string $token   Authentication token.
	 * @return string Role name, defaults to 'user'.
	 */
	public static function get_user_role( $user_id, $token ) {
		$default_role = 'user';

		if ( ! self::is_user_authenticated() ) {
			return $default_role;
		}

		$api      = new Api();
		$response = $api->get( 'api', 'users/' . $user_id, array(), $token );

		if ( is_wp_error( $response ) ) {
			Logger::add_log( 'get_user_role: API error - ' . $response->get_error_message(), 'include/helpers', 'error' );
			return $default_role;
		}

		if ( ! is_array( $response ) ) {
			Logger::add_log( 'get_user_role: unexpected response type', 'include/helpers', 'error' );
			return $default_role;
		}

		if (
			isset( $response['userGroups'] )
			&& is_array( $response['userGroups'] )
			&& ! empty( $response['userGroups'][0]['role']['name'] )
			&& is_string( $response['userGroups'][0]['role']['name'] )
		) {
			return $response['userGroups'][0]['role']['name'];
		}

		return $default_role;
	}

	/**
	 * Extract id from iri
	 *
	 * @since 3.0.0
	 * @param string $iri
	 * @return string
	 */
	public static function extract_id( $iri ) {
		$iri_parts = explode( '/', $iri );
		return end( $iri_parts );
	}

	/**
	 * Get user id from API and save it in options
	 *
	 * @since 3.2.0
	 * @return void
	 */
	public static function get_user_id() {
		$api = new Api();

		$token = Options::get( 'settings-analytics-token', '' );
		if ( ! is_string( $token ) ) {
			$token = '';
		}

		$response = $api->get( 'api', 'authentication/auth', array(), $token, true );

		if ( is_wp_error( $response ) || ! is_array( $response ) || ! self::is_valid_user( $response ) ) {
			Options::delete( 'settings-analytics-token' );
			Options::delete( 'settings-analytics-userid' );

			return;
		}

		// PHPStan: After is_valid_user validation, we know $response['user']['@id'] is a string
		/** @var string $user_iri */
		$user_iri = $response['user']['@id'];
		$user_id  = self::extract_id( $user_iri );

		Options::update( 'settings-analytics-userid', $user_id );
	}

	/**
	 * Validate user response structure
	 *
	 * @since 3.2.0
	 * @param array<mixed, mixed> $response
	 * @return bool
	 */
	private static function is_valid_user( array $response ) {
		return isset( $response['user'] )
		&& is_array( $response['user'] )
		&& isset( $response['user']['@id'] )
		&& is_string( $response['user']['@id'] );
	}

	/**
	 * Check if user is authenticated with Actirise
	 *
	 * @since 3.3
	 * @return bool
	 */
	public static function is_user_authenticated() {
		/** @var string $token */
		$token = Options::get( 'settings-analytics-token', '' );
		/** @var string $user_id */
		$user_id = Options::get( 'settings-analytics-userid', '' );

		return ! empty( $token ) && is_string( $token ) && ! empty( $user_id ) && is_string( $user_id );
	}

	/**
	 * Apply custom value filter
	 *
	 * @since 3.1.0
	 * @param string $custom_name
	 * @param string $value
	 * @return string
	 */
	private static function apply_custom_value_filter( $custom_name, $value ) {
		$filter_name = $custom_name . '_value';

		/** @var string|null $filter_value */
		$filter_value = apply_filters( 'actirise_' . $filter_name, $value );

		if ( ! empty( $filter_value ) ) {
			/** @var string $filter_value */
			return sanitize_text_field( $filter_value );
		}

		/** @var string $value */
		return $value;
	}

	/**
	 * Test if XPath expression works against HTML content
	 *
	 * @since 2.0.0
	 * @param \DOMDocument $dom DOMDocument object containing the HTML content.
	 * @param string       $xpath_expression XPath expression to validate.
	 * @return array{success: bool, found_elements: int, elements?: \DOMNodeList<\DOMNode>, error: string|null}
	 */
	public static function test_xpath( $dom, $xpath_expression ) {
		if ( empty( $dom ) || empty( $xpath_expression ) ) {
			return array(
				'success'        => false,
				'found_elements' => 0,
				'error'          => 'HTML content or XPath expression is empty',
			);
		}

		try {
			$xpath_dom = new \DOMXPath( $dom );
			$result    = $xpath_dom->query( $xpath_expression );

			if ( $result === false ) {
				return array(
					'success'        => false,
					'found_elements' => 0,
					'error'          => 'Invalid XPath expression',
				);
			}

			/** @var \DOMNodeList<\DOMNode> $result */
			$response = array(
				'success'        => true,
				'found_elements' => $result->length,
				'error'          => null,
				'elements'       => $result,
			);

			return $response;
		} catch ( \Exception $e ) {
			return array(
				'success'        => false,
				'found_elements' => 0,
				'error'          => $e->getMessage(),
			);
		}
	}

	/**
	 * Resolve a URL to its corresponding content (post, page, category, or tag).
	 *
	 * @since 3.3.3
	 * @param string $url The URL to resolve.
	 * @return \stdClass The resolved content object.
	 */
	public static function resolve_url_to_content( $url ) {
		$result = new \stdClass();

		// 1. Try to resolve as a post/page.
		$post_id = url_to_postid( $url );
		if ( $post_id !== 0 ) {
			$post = get_post( $post_id );
			if ( $post !== null ) {
				return self::build_post_result( $post );
			}
		}

		// 2. Parse query string parameters.
		$parsed_url   = wp_parse_url( $url );
		$query_params = array();
		if ( isset( $parsed_url['query'] ) ) {
			parse_str( $parsed_url['query'], $query_params );
		}

		// 3. Check for category by ID (?cat=ID).
		if ( isset( $query_params['cat'] ) ) {
			$category = get_term( absint( $query_params['cat'] ), 'category' );
			if ( $category !== null && ! is_wp_error( $category ) ) {
				return self::build_term_result( $category, 'category' );
			}
		}

		// 4. Check for tag by slug (?tag=slug).
		if ( isset( $query_params['tag'] ) && is_string( $query_params['tag'] ) ) {
			$tag = get_term_by( 'slug', sanitize_text_field( $query_params['tag'] ), 'post_tag' );
			if ( $tag instanceof \WP_Term ) {
				return self::build_term_result( $tag, 'post_tag' );
			}
		}

		// 5. Fallback: Check for category/tag by slug in URL path (pretty permalinks).
		$slug = self::extract_slug_from_url( $url );
		if ( ! empty( $slug ) ) {
			$category = get_term_by( 'slug', $slug, 'category' );
			if ( $category instanceof \WP_Term ) {
				return self::build_term_result( $category, 'category' );
			}

			$tag = get_term_by( 'slug', $slug, 'post_tag' );
			if ( $tag instanceof \WP_Term ) {
				return self::build_term_result( $tag, 'post_tag' );
			}
		}

		return $result;
	}

	/**
	 * Build a result object from a post.
	 *
	 * @since 3.3.3
	 * @param \WP_Post $post The post object.
	 * @return \stdClass The result object.
	 */
	public static function build_post_result( $post ) {
		$result                = new \stdClass();
		$result->id            = $post->ID;
		$result->title         = $post->post_title;
		$result->publishedDate = $post->post_date;
		$result->summary       = $post->post_type === 'page' ? '' : substr( $post->post_content, 0, 100 );
		$result->image         = get_the_post_thumbnail_url( $post->ID );
		$result->type          = $post->post_type;

		return $result;
	}

	/**
	 * Build a result object from a taxonomy term.
	 *
	 * @since 3.3.3
	 * @param \WP_Term $term The term object.
	 * @param string   $type The term type (category or post_tag).
	 * @return \stdClass The result object.
	 */
	public static function build_term_result( $term, $type ) {
		$result                = new \stdClass();
		$result->id            = $term->term_id;
		$result->title         = $term->name;
		$result->publishedDate = '';
		$result->summary       = $term->description;
		$result->image         = null;
		$result->type          = $type;

		return $result;
	}

	/**
	 * Extract the slug from a URL path.
	 *
	 * @since 3.3.3
	 * @param string $url The URL to parse.
	 * @return string|null The extracted slug or null.
	 */
	public static function extract_slug_from_url( $url ) {
		$slug = explode( '/', $url );
		$slug = array_filter( $slug );

		if ( empty( $slug ) ) {
			return null;
		}

		$slug = end( $slug );

		// Remove any query string from the slug.
		if ( strpos( $slug, '?' ) !== false ) {
			$slug = substr( $slug, 0, strpos( $slug, '?' ) );
		}

		return ! empty( $slug ) ? $slug : null;
	}
}
