<?php

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

require_once __DIR__ . '/class-content-miner-rendered-snapshot.php';

/**
 * Reads editorial content through WordPress APIs rather than querying tables.
 */
class Content_Miner_Repository {
	const MAX_ITEMS = 500;
	const MEMORY_BATCH_SIZE = 100;

	/** @var Content_Miner_Content_Extractor */
	private $extractor;

	/** @var Content_Miner_Scoring_Exclusions|null */
	private $scoring_exclusions;

	/** @var int */
	private $page_limit;

	/**
	 * @param Content_Miner_Content_Extractor|null $extractor Content extractor.
	 * @param int|null                              $page_limit Licensed page ceiling.
	 * @param Content_Miner_Scoring_Exclusions|null $scoring_exclusions Scoring exclusions.
	 */
	public function __construct( $extractor = null, $page_limit = null, $scoring_exclusions = null ) {
		$this->extractor = $extractor ? $extractor : new Content_Miner_Content_Extractor();
		$this->scoring_exclusions = $scoring_exclusions ? $scoring_exclusions : ( class_exists( 'Content_Miner_Scoring_Exclusions' ) ? new Content_Miner_Scoring_Exclusions() : null );
		if ( null === $page_limit ) {
			$this->page_limit = function_exists( 'content_miner_license' ) ? content_miner_license()->get_page_limit() : PHP_INT_MAX;
		} else {
			$this->page_limit = max( 1, (int) $page_limit );
		}
	}


	/** Refresh the one site-wide sitemap snapshot at synchronization start. @return array<string,mixed> */
	public function refresh_scoring_context() {
		return $this->scoring_exclusions
			? $this->scoring_exclusions->refresh_sitemap()
			: array( 'changed' => false, 'snapshot' => array( 'membership_known' => false, 'status' => 'unavailable', 'reason' => 'classifier_unavailable' ) );
	}

	/** Return the shared scoring exclusion service. @return Content_Miner_Scoring_Exclusions|null */
	public function get_scoring_exclusions() {
		return $this->scoring_exclusions;
	}

	/**
	 * Return analyzable public post types.
	 *
	 * @return string[]
	 */
	public function get_post_types() {
		$types = get_post_types(
			array(
				'public'  => true,
				'show_ui' => true,
			),
			'names'
		);

		return array_values( array_diff( $types, array( 'attachment' ) ) );
	}

	/**
	 * Build a normalized inventory for analysis.
	 *
	 * @return array<int,array<string,mixed>>
	 */
	public function get_inventory() {
		$query = new WP_Query(
			array(
				'post_type'              => $this->get_post_types(),
				'post_status'            => array( 'publish', 'draft', 'future', 'private' ),
				'posts_per_page'         => self::MAX_ITEMS,
				'orderby'                => 'modified',
				'order'                  => 'DESC',
				'no_found_rows'          => true,
				'update_post_meta_cache' => true,
				'update_post_term_cache' => false,
			)
		);

		$items = array_map( array( $this, 'format_post' ), $query->posts );

		wp_reset_postdata();

		return $items;
	}

	/**
	 * Return the next resumable memory batch by ascending post ID.
	 *
	 * @param int $after_id Process IDs greater than this value.
	 * @param int $limit Batch size.
	 * @return array<int,array<string,mixed>>
	 */
	public function get_inventory_batch( $after_id = 0, $limit = self::MEMORY_BATCH_SIZE ) {
		global $wpdb;

		$after_id = max( 0, (int) $after_id );
		$limit    = min( 500, max( 10, (int) $limit ) );
		$max_id = $this->get_max_id();
		$where_filter = function ( $where ) use ( $after_id, $max_id, $wpdb ) {
			return $where . $wpdb->prepare( " AND {$wpdb->posts}.ID > %d AND {$wpdb->posts}.ID <= %d", $after_id, $max_id );
		};

		add_filter( 'posts_where', $where_filter );
		$query = new WP_Query(
			array(
				'post_type'              => $this->get_post_types(),
				'post_status'            => $this->get_active_statuses(),
				'posts_per_page'         => $limit,
				'orderby'                => 'ID',
				'order'                  => 'ASC',
				'no_found_rows'          => true,
				'suppress_filters'       => false,
				'update_post_meta_cache' => true,
				'update_post_term_cache' => false,
			)
		);
		remove_filter( 'posts_where', $where_filter );

		$items = array_map( array( $this, 'format_post' ), $query->posts );
		wp_reset_postdata();

		return $items;
	}

	/** Return the next published-page batch for rendered verification. @return array<int,array<string,mixed>> */
	public function get_published_batch( $after_id = 0, $limit = 25, $max_id = 0 ) {
		global $wpdb;
		$after_id = max( 0, (int) $after_id );
		$limit = min( 25, max( 1, (int) $limit ) );
		$max_id = $max_id > 0 ? (int) $max_id : $this->get_max_id();
		$where_filter = function ( $where ) use ( $after_id, $max_id, $wpdb ) {
			return $where . $wpdb->prepare( " AND {$wpdb->posts}.ID > %d AND {$wpdb->posts}.ID <= %d", $after_id, $max_id );
		};
		add_filter( 'posts_where', $where_filter );
		$query = new WP_Query(
			array(
				'post_type' => $this->get_post_types(), 'post_status' => 'publish', 'posts_per_page' => $limit,
				'orderby' => 'ID', 'order' => 'ASC', 'no_found_rows' => true, 'suppress_filters' => false,
				'update_post_meta_cache' => true, 'update_post_term_cache' => false,
			)
		);
		remove_filter( 'posts_where', $where_filter );
		$items = array_map( array( $this, 'format_post' ), $query->posts );
		wp_reset_postdata();
		return $items;
	}

	/** @return int */
	public function get_published_total() {
		$total = 0;
		foreach ( $this->get_post_types() as $post_type ) {
			$counts = wp_count_posts( $post_type );
			$total += isset( $counts->publish ) ? (int) $counts->publish : 0;
		}
		return min( $total, $this->page_limit );
	}

	/** @return int */
	public function get_published_max_id() {
		global $wpdb;
		$max_id = $this->get_max_id();
		$where_filter = function ( $where ) use ( $max_id, $wpdb ) {
			return $where . $wpdb->prepare( " AND {$wpdb->posts}.ID <= %d", $max_id );
		};
		add_filter( 'posts_where', $where_filter );
		$query = new WP_Query( array( 'post_type' => $this->get_post_types(), 'post_status' => 'publish', 'posts_per_page' => 1, 'orderby' => 'ID', 'order' => 'DESC', 'fields' => 'ids', 'no_found_rows' => true, 'suppress_filters' => false ) );
		remove_filter( 'posts_where', $where_filter );
		return $query->posts ? (int) $query->posts[0] : 0;
	}

	/**
	 * Return one post for dirty-queue analysis, including removed statuses.
	 *
	 * @param int $post_id Post ID.
	 * @return array<string,mixed>|null
	 */
	public function get_item( $post_id ) {
		if ( ! $this->is_id_within_limit( $post_id ) ) {
			return null;
		}
		$post = get_post( (int) $post_id );
		if ( ! $post || ! in_array( $post->post_type, $this->get_post_types(), true ) ) {
			return null;
		}

		return $this->format_post( $post );
	}

	/** Return the front-end permalink or an authenticated preview URL. @return string */
	public static function get_post_view_url( $post_id ) {
		$post = get_post( (int) $post_id );
		if ( ! $post ) {
			return '';
		}

		if ( 'publish' === $post->post_status ) {
			return (string) get_permalink( $post );
		}

		$preview_url = get_preview_post_link( $post );
		return $preview_url ? (string) $preview_url : (string) get_permalink( $post );
	}

	/**
	 * Count active analyzable records without loading them.
	 *
	 * @return int
	 */
	public function get_total_count() {
		return min( $this->get_uncapped_total_count(), $this->page_limit );
	}

	/** Count all eligible records before applying the licensed ceiling. */
	public function get_uncapped_total_count() {
		$total = 0;
		foreach ( $this->get_post_types() as $post_type ) {
			$counts = wp_count_posts( $post_type );
			foreach ( $this->get_active_statuses() as $status ) {
				$total += isset( $counts->{$status} ) ? (int) $counts->{$status} : 0;
			}
		}

		return $total;
	}

	/**
	 * Count eligible records created after a completed memory checkpoint.
	 *
	 * @param int $after_id Previous maximum post ID.
	 * @return int
	 */
	public function get_count_after_id( $after_id ) {
		global $wpdb;

		$after_id     = max( 0, (int) $after_id );
		$max_id       = $this->get_max_id();
		$where_filter = function ( $where ) use ( $after_id, $max_id, $wpdb ) {
			return $where . $wpdb->prepare( " AND {$wpdb->posts}.ID > %d AND {$wpdb->posts}.ID <= %d", $after_id, $max_id );
		};

		add_filter( 'posts_where', $where_filter );
		$query = new WP_Query(
			array(
				'post_type'              => $this->get_post_types(),
				'post_status'            => $this->get_active_statuses(),
				'posts_per_page'         => 1,
				'fields'                 => 'ids',
				'no_found_rows'          => false,
				'suppress_filters'       => false,
				'update_post_meta_cache' => false,
				'update_post_term_cache' => false,
			)
		);
		remove_filter( 'posts_where', $where_filter );

		return (int) $query->found_posts;
	}

	/**
	 * Capture the highest eligible ID at job start so ingestion has a stable end.
	 *
	 * @return int
	 */
	public function get_max_id() {
		if ( PHP_INT_MAX !== $this->page_limit && $this->get_uncapped_total_count() > $this->page_limit ) {
			$query = new WP_Query(
				array(
					'post_type'      => $this->get_post_types(),
					'post_status'    => $this->get_active_statuses(),
					'posts_per_page' => 1,
					'offset'         => $this->page_limit - 1,
					'orderby'        => 'ID',
					'order'          => 'ASC',
					'fields'         => 'ids',
					'no_found_rows'  => true,
				)
			);
			return $query->posts ? (int) $query->posts[0] : 0;
		}

		$query = new WP_Query(
			array(
				'post_type'      => $this->get_post_types(),
				'post_status'    => $this->get_active_statuses(),
				'posts_per_page' => 1,
				'orderby'        => 'ID',
				'order'          => 'DESC',
				'fields'         => 'ids',
				'no_found_rows'  => true,
			)
		);

		return $query->posts ? (int) $query->posts[0] : 0;
	}

	/** Return the active licensed page ceiling. */
	public function get_page_limit() {
		return $this->page_limit;
	}

	/** Return whether eligible source content exceeds the licensed ceiling. */
	public function is_limit_reached() {
		return PHP_INT_MAX !== $this->page_limit && $this->get_uncapped_total_count() > $this->page_limit;
	}

	/** Return whether one post belongs to the licensed ascending-ID window. */
	private function is_id_within_limit( $post_id ) {
		if ( PHP_INT_MAX === $this->page_limit || ! $this->is_limit_reached() ) {
			return true;
		}
		return absint( $post_id ) <= $this->get_max_id();
	}

	/**
	 * @return string[]
	 */
	private function get_active_statuses() {
		return array( 'publish', 'draft', 'future', 'private' );
	}

	/**
	 * @param WP_Post $post Post.
	 * @return array<string,mixed>
	 */
	public function format_post( $post ) {
		$stored    = $this->extractor->extract( $post );
		$canonical = $this->get_canonical_url( $post->ID );
		$url       = get_permalink( $post );
		$memory    = get_post_meta( $post->ID, '_content_miner_memory', true );
		$verification = is_array( $memory ) && isset( $memory['verification'] ) && is_array( $memory['verification'] )
			? $memory['verification']
			: array();
		$rendered_text = class_exists( 'Content_Miner_Rendered_Verification' )
			? get_post_meta( $post->ID, Content_Miner_Rendered_Verification::TEXT_META_KEY, true )
			: '';
		$stored_signals = $this->extract_signals( $post, $stored['html'], $canonical );
		$selection = self::select_analysis_content(
			$stored['text'],
			$stored['warnings'],
			$verification,
			$rendered_text,
			$post->post_modified_gmt,
			$post->post_status,
			$url,
			$stored_signals
		);
		$scoring = $this->scoring_exclusions
			? $this->scoring_exclusions->classify( $post )
			: array( 'excluded' => false, 'scored' => true, 'automatic_reasons' => array(), 'primary_reason' => '', 'override' => '' );
		$content_sources = $stored['sources'];
		if ( 'rendered' === $selection['analysis_content_source'] ) {
			$content_sources = array( __( 'Rendered page', 'content-miner' ) );
		} elseif ( 'stored_fallback' === $selection['analysis_content_source'] ) {
			$content_sources[] = __( 'Stored fallback', 'content-miner' );
		} elseif ( 'stored_nonpublic' === $selection['analysis_content_source'] ) {
			$content_sources[] = __( 'Stored non-public record', 'content-miner' );
		}

		return array(
			'id'            => (int) $post->ID,
			'title'         => get_the_title( $post ),
			'analysis_title'=> $selection['analysis_title'] ? $selection['analysis_title'] : get_the_title( $post ),
			'url'           => $url,
			'edit_url'      => get_edit_post_link( $post->ID, 'raw' ),
			'post_type'     => $post->post_type,
			'status'        => $post->post_status,
			'published_at'  => 'rendered' === $selection['analysis_content_source'] ? $selection['published_at'] : $post->post_date_gmt,
			'modified_at'   => 'rendered' === $selection['analysis_content_source'] ? $selection['modified_at'] : $post->post_modified_gmt,
			'source_modified_at' => $post->post_modified_gmt,
			'stored_content'=> $stored['text'],
			'content'       => $selection['content'],
			'analysis_content_source' => $selection['analysis_content_source'],
			'content_sources' => array_values( array_unique( $content_sources ) ),
			'stored_extraction_warnings' => $stored['warnings'],
			'extraction_warnings' => $selection['extraction_warnings'],
			'verification'  => $selection['verification'],
			'excerpt'       => get_the_excerpt( $post ),
			'canonical_url' => $canonical,
			'search_console'=> $this->get_search_console_metrics( $post->ID ),
			'analytics'     => $this->get_analytics_metrics( $post->ID ),
			'scoring'       => $scoring,
			'signals'       => $selection['signals'],
		);
	}

	/** Select one explicitly sourced body and signal set for every engine path. */
	public static function select_analysis_content( $stored_content, array $warnings, array $verification, $rendered_text = '', $source_modified_at = '', $source_status = '', $url = '', array $stored_signals = array() ) {
		$legacy_count_only = $verification
			&& ! array_key_exists( 'stored_content_hash', $verification )
			&& ! array_key_exists( 'source_modified_at', $verification )
			&& ! array_key_exists( 'source_status', $verification );
		$current = Content_Miner_Rendered_Snapshot::is_current(
			$verification,
			$stored_content,
			$url,
			$source_status,
			$source_modified_at
		);
		$use_rendered = $current
			&& 'rendered' === ( isset( $verification['input_source'] ) ? $verification['input_source'] : '' )
			&& array_key_exists( 'rendered_text_available', $verification )
			&& true === $verification['rendered_text_available']
			&& is_string( $rendered_text )
			&& '' !== trim( $rendered_text )
			&& isset( $verification['rendered_text_hash'] )
			&& is_string( $verification['rendered_text_hash'] )
			&& hash_equals( $verification['rendered_text_hash'], hash( 'sha256', $rendered_text ) )
			&& isset( $verification['signals'] )
			&& is_array( $verification['signals'] );

		if ( 'publish' !== $source_status ) {
			$source = 'stored_nonpublic';
			$verification = array(
				'snapshot_schema' => Content_Miner_Rendered_Snapshot::SCHEMA_VERSION,
				'parser_version' => Content_Miner_Rendered_Snapshot::PARSER_VERSION,
				'status' => 'nonpublic',
				'input_source' => $source,
				'fallback_reason_code' => '',
				'reason' => __( 'Stored content was used because this record is not public.', 'content-miner' ),
				'stored_content_hash' => self::hash_analysis_content( $stored_content ),
				'source_fingerprint' => Content_Miner_Rendered_Snapshot::source_fingerprint( $stored_content, $url, $source_status, $source_modified_at ),
				'source_modified_at' => (string) $source_modified_at,
				'source_status' => (string) $source_status,
				'checked_at' => gmdate( 'c' ),
			);
		} elseif ( $use_rendered ) {
			$source = 'rendered';
		} else {
			$source = 'stored_fallback';
			if ( ! $current || 'stored_fallback' !== ( isset( $verification['input_source'] ) ? $verification['input_source'] : '' ) ) {
				$fallback = array(
					'snapshot_schema' => Content_Miner_Rendered_Snapshot::SCHEMA_VERSION,
					'parser_version' => Content_Miner_Rendered_Snapshot::PARSER_VERSION,
					'status' => 'fallback',
					'input_source' => $source,
					'fallback_reason_code' => 'snapshot_missing',
					'reason' => __( 'Rendered input has not been collected for this page, so stored content was used.', 'content-miner' ),
					'stored_content_hash' => self::hash_analysis_content( $stored_content ),
					'source_fingerprint' => Content_Miner_Rendered_Snapshot::source_fingerprint( $stored_content, $url, $source_status, $source_modified_at ),
					'source_modified_at' => (string) $source_modified_at,
					'source_status' => (string) $source_status,
					'checked_at' => '',
				);
				if ( $legacy_count_only ) {
					// Preserve the measured count-only evidence from 1.3.8 while
					// explicitly marking the selected analysis body as stored fallback.
					$fallback = array_merge( $fallback, $verification );
					$fallback['snapshot_schema'] = Content_Miner_Rendered_Snapshot::SCHEMA_VERSION;
					$fallback['parser_version'] = Content_Miner_Rendered_Snapshot::PARSER_VERSION;
					$fallback['input_source'] = $source;
					$fallback['fallback_reason_code'] = 'legacy_count_only';
					$fallback['stored_content_hash'] = self::hash_analysis_content( $stored_content );
					$fallback['source_fingerprint'] = Content_Miner_Rendered_Snapshot::source_fingerprint( $stored_content, $url, $source_status, $source_modified_at );
					$fallback['source_modified_at'] = (string) $source_modified_at;
					$fallback['source_status'] = (string) $source_status;
				}
				$verification = $fallback;
			}
		}

		return array(
			'content'                 => $use_rendered ? $rendered_text : (string) $stored_content,
			'analysis_content_source' => $source,
			'extraction_warnings'     => $use_rendered ? array() : $warnings,
			'analysis_title'          => $use_rendered && isset( $verification['analysis_title'] ) ? (string) $verification['analysis_title'] : '',
			'published_at'            => $use_rendered && isset( $verification['published_at'] ) ? (string) $verification['published_at'] : '',
			'modified_at'             => $use_rendered && isset( $verification['modified_at'] ) ? (string) $verification['modified_at'] : '',
			'signals'                 => $use_rendered ? $verification['signals'] : $stored_signals,
			'verification'            => $verification,
		);
	}

	/** Hash normalized stored extraction so a later edit cannot reuse stale rendered text. */
	public static function hash_analysis_content( $content ) {
		return Content_Miner_Rendered_Snapshot::content_hash( $content );
	}

	/** @return array<string,mixed> */
	private function get_search_console_metrics( $post_id ) {
		$metrics = get_post_meta( $post_id, '_content_miner_search_console', true );
		return is_array( $metrics ) ? $metrics : array();
	}

	/** @return array<string,mixed> */
	private function get_analytics_metrics( $post_id ) {
		$metrics = get_post_meta( $post_id, '_content_miner_google_analytics', true );
		return is_array( $metrics ) ? $metrics : array();
	}

	/**
	 * Extract CMS-specific technical evidence for the portable readiness engine.
	 *
	 * @param WP_Post $post Post.
	 * @param string  $html Rendered stored content.
	 * @param string  $canonical Configured canonical.
	 * @return array<string,mixed>
	 */
	private function extract_signals( $post, $html, $canonical ) {
		$headings = array();
		preg_match_all( '/<h([1-6])\b[^>]*>(.*?)<\/h\1>/is', $html, $headings, PREG_SET_ORDER );
		$heading_counts    = array_fill( 1, 6, 0 );
		$question_headings = 0;
		foreach ( $headings as $heading ) {
			++$heading_counts[ (int) $heading[1] ];
			if ( preg_match( '/\?\s*$/', trim( wp_strip_all_tags( $heading[2] ) ) ) ) {
				++$question_headings;
			}
		}

		$internal_links = 0;
		$external_links = 0;
		$home_host      = strtolower( (string) wp_parse_url( home_url( '/' ), PHP_URL_HOST ) );
		$links          = array();
		preg_match_all( '/<a\b[^>]*\bhref\s*=\s*(["\'])(.*?)\1/is', $html, $links );
		foreach ( isset( $links[2] ) ? $links[2] : array() as $href ) {
			$href = trim( html_entity_decode( $href, ENT_QUOTES | ENT_HTML5, 'UTF-8' ) );
			if ( '' === $href || 0 === strpos( $href, '#' ) || 0 === strpos( $href, 'mailto:' ) || 0 === strpos( $href, 'tel:' ) ) {
				continue;
			}
			$host = strtolower( (string) wp_parse_url( $href, PHP_URL_HOST ) );
			if ( '' === $host || $host === $home_host ) {
				++$internal_links;
			} else {
				++$external_links;
			}
		}

		$images             = array();
		$images_missing_alt = 0;
		preg_match_all( '/<img\b[^>]*>/is', $html, $images );
		foreach ( isset( $images[0] ) ? $images[0] : array() as $image ) {
			if ( ! preg_match( '/\balt\s*=\s*(["\'])[^"\']+\1/is', $image ) ) {
				++$images_missing_alt;
			}
		}

		$author = get_userdata( $post->post_author );

		return array(
			'seo_title'          => $this->first_meta_value( $post->ID, array( '_yoast_wpseo_title', 'rank_math_title', '_aioseo_title' ) ),
			'meta_description'   => $this->first_meta_value( $post->ID, array( '_yoast_wpseo_metadesc', 'rank_math_description', '_aioseo_description' ) ),
			'effective_canonical'=> $canonical ? $canonical : get_permalink( $post ),
			'indexable'          => 'publish' === $post->post_status && ! $this->is_noindex( $post->ID ),
			'h1_count'           => $heading_counts[1],
			'h2_count'           => $heading_counts[2],
			'h3_count'           => $heading_counts[3],
			'question_headings'  => $question_headings,
			'internal_links'     => $internal_links,
			'external_links'     => $external_links,
			'image_count'        => count( isset( $images[0] ) ? $images[0] : array() ),
			'images_missing_alt' => $images_missing_alt,
			'list_count'         => preg_match_all( '/<(?:ul|ol)\b/i', $html ),
			'table_count'        => preg_match_all( '/<table\b/i', $html ),
			'schema_present'     => $this->has_schema_configuration( $post->ID, $post->post_content ),
			'author_name'        => $author ? $author->display_name : '',
			'author_bio'         => $author ? (string) get_user_meta( $author->ID, 'description', true ) : '',
		);
	}

	/** @return string */
	private function first_meta_value( $post_id, array $keys ) {
		foreach ( $keys as $key ) {
			$value = get_post_meta( $post_id, $key, true );
			if ( is_scalar( $value ) && '' !== trim( (string) $value ) ) {
				return (string) $value;
			}
		}
		return '';
	}

	/** @return bool */
	private function is_noindex( $post_id ) {
		if ( $this->scoring_exclusions ) {
			return $this->scoring_exclusions->is_noindex( $post_id );
		}
		if ( '1' === (string) get_post_meta( $post_id, '_yoast_wpseo_meta-robots-noindex', true ) ) {
			return true;
		}
		$rank_math = get_post_meta( $post_id, 'rank_math_robots', true );
		if ( is_array( $rank_math ) && in_array( 'noindex', $rank_math, true ) ) {
			return true;
		}
		return (bool) get_post_meta( $post_id, '_aioseo_robots_noindex', true );
	}

	/** @return bool */
	private function has_schema_configuration( $post_id, $raw_content ) {
		if ( false !== stripos( (string) $raw_content, 'application/ld+json' ) || false !== stripos( (string) $raw_content, 'yoast/faq-block' ) ) {
			return true;
		}
		foreach ( array_keys( get_post_meta( $post_id ) ) as $key ) {
			if ( false !== stripos( $key, 'schema' ) ) {
				$value = get_post_meta( $post_id, $key, true );
				if ( ! empty( $value ) ) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Read canonical values from common SEO plugins when available.
	 *
	 * @param int $post_id Post ID.
	 * @return string
	 */
	private function get_canonical_url( $post_id ) {
		$keys = array(
			'_yoast_wpseo_canonical',
			'rank_math_canonical_url',
			'_aioseo_canonical_url',
		);

		foreach ( $keys as $key ) {
			$value = get_post_meta( $post_id, $key, true );
			if ( is_string( $value ) && '' !== trim( $value ) ) {
				return esc_url_raw( $value );
			}
		}

		return '';
	}
}
