<?php
/**
 * Costudio_Url — shared URL utilities.
 *
 * Vertex grounding-redirect resolution lives here so BOTH the References
 * Renderer (at render time) and the Gemini search engine (at search time) can
 * resolve `vertexaisearch.cloud.google.com/grounding-api-redirect/…` URLs to
 * their canonical destination. They share the same `costudio_vref_*` transient
 * cache, so a redirect resolved once is reused everywhere.
 *
 * @package Contentosapp_Studio
 * @since   1.2.6
 */

defined( 'ABSPATH' ) || exit;

class Costudio_Url {

	const VERTEX_REDIRECT_HOST = 'vertexaisearch.cloud.google.com';
	const VERTEX_RESOLVE_TTL   = 30 * DAY_IN_SECONDS;
	const NEGATIVE_CACHE_TTL   = 5 * MINUTE_IN_SECONDS;
	const HEAD_TIMEOUT         = 8;
	const MAX_HOPS             = 8;
	const USER_AGENT           = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36';

	/**
	 * Whether a URL is a Vertex grounding-redirect.
	 */
	public static function is_vertex_redirect( string $url ): bool {
		return '' !== $url && false !== strpos( $url, self::VERTEX_REDIRECT_HOST );
	}

	/**
	 * Follow a Vertex grounding-redirect chain to its canonical destination.
	 * Returns the resolved URL, or '' when it cannot be resolved. Cached for
	 * 30 days on success, short cache on failure.
	 */
	public static function resolve_vertex_redirect( string $url ): string {
		$key    = 'costudio_vref_' . md5( $url );
		$cached = get_transient( $key );
		if ( false !== $cached ) {
			return (string) $cached;
		}

		$current = costudio_safe_http_url( $url );
		if ( '' === $current ) {
			set_transient( $key, '', self::NEGATIVE_CACHE_TTL );
			return '';
		}
		$hops = 0;

		while ( $hops < self::MAX_HOPS ) {
			$resp = wp_safe_remote_head(
				$current,
				costudio_safe_remote_args( array(
					'timeout'     => self::HEAD_TIMEOUT,
					'redirection' => 0,
					'user-agent'  => self::USER_AGENT,
				) )
			);

			if ( is_wp_error( $resp ) ) {
				set_transient( $key, '', self::NEGATIVE_CACHE_TTL );
				return '';
			}

			$code = (int) wp_remote_retrieve_response_code( $resp );
			if ( $code >= 300 && $code < 400 ) {
				$location = wp_remote_retrieve_header( $resp, 'location' );
				if ( ! $location ) {
					break;
				}
				$current = costudio_safe_http_url( self::absolutize_url( (string) $location, $current ) );
				if ( '' === $current ) {
					set_transient( $key, '', self::NEGATIVE_CACHE_TTL );
					return '';
				}
				$hops++;
				continue;
			}

			break;
		}

		if ( false !== strpos( $current, self::VERTEX_REDIRECT_HOST ) ) {
			set_transient( $key, '', self::NEGATIVE_CACHE_TTL );
			return '';
		}

		set_transient( $key, $current, self::VERTEX_RESOLVE_TTL );
		return $current;
	}

	private static function absolutize_url( string $location, string $base ): string {
		if ( '' === $location ) {
			return $base;
		}
		if ( preg_match( '#^https?://#i', $location ) ) {
			return $location;
		}
		$parts = wp_parse_url( $base );
		if ( ! $parts || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) {
			return $location;
		}
		$origin = $parts['scheme'] . '://' . $parts['host']
			. ( isset( $parts['port'] ) ? ':' . $parts['port'] : '' );
		if ( '/' === $location[0] ) {
			return $origin . $location;
		}
		$path = isset( $parts['path'] ) ? preg_replace( '#/[^/]*$#', '/', $parts['path'] ) : '/';
		return $origin . $path . $location;
	}
}
