Code/Endpoints/NetworkStorageEndpointUrlSupport.cs
using System;
using System.Collections.Generic;

namespace Sandbox;

public static partial class NetworkStorage
{
	private sealed class EndpointReference
	{
		public string Raw { get; init; }
		public string Slug { get; init; }
		public bool IsUrl { get; init; }
		public string ProjectId { get; init; }
		public string PublicApiKey { get; init; }
		public string BaseUrl { get; init; }
		public string ApiVersion { get; init; }
		public bool SecretKeyRequested { get; init; }
	}

	private static EndpointReference ResolveEndpointReference( string endpoint )
	{
		var raw = endpoint?.Trim() ?? "";
		if ( !Uri.TryCreate( raw, UriKind.Absolute, out var uri ) ||
			(uri.Scheme != Uri.UriSchemeHttps && uri.Scheme != Uri.UriSchemeHttp) )
		{
			return new EndpointReference { Raw = raw, Slug = raw };
		}

		var query = ParseQueryString( uri.Query );
		var segments = uri.AbsolutePath.Split( '/', StringSplitOptions.RemoveEmptyEntries );
		var endpointsIndex = Array.FindIndex( segments, s => string.Equals( s, "endpoints", StringComparison.OrdinalIgnoreCase ) );
		var projectId = endpointsIndex >= 0 && endpointsIndex + 1 < segments.Length
			? Uri.UnescapeDataString( segments[endpointsIndex + 1] )
			: "";
		var slug = endpointsIndex >= 0 && endpointsIndex + 2 < segments.Length
			? Uri.UnescapeDataString( segments[endpointsIndex + 2] )
			: ReadQueryValue( query, "slug", "endpoint", "endpointSlug", "_endpointSlug" );
		var apiVersion = endpointsIndex > 0 && IsApiVersionSegment( segments[endpointsIndex - 1] )
			? Uri.UnescapeDataString( segments[endpointsIndex - 1] )
			: null;
		var baseUrl = BuildBaseUrlFromEndpointUri( uri, segments, endpointsIndex, apiVersion );
		var publicApiKey = ReadQueryValue( query, "apiKey", "publicKey", "public-key", "x-public-key" );
		var secretRequested = query.ContainsKey( "secret-key" );
		foreach ( var key in DedicatedSecretLaunchKeys )
			secretRequested |= query.ContainsKey( key );

		return new EndpointReference
		{
			Raw = raw,
			Slug = slug,
			IsUrl = true,
			ProjectId = projectId,
			PublicApiKey = publicApiKey,
			BaseUrl = baseUrl,
			ApiVersion = apiVersion,
			SecretKeyRequested = secretRequested
		};
	}

	private static void ApplyEndpointReferenceConfiguration( EndpointReference endpoint )
	{
		if ( endpoint?.IsUrl != true ) return;

		if ( string.IsNullOrWhiteSpace( endpoint.Slug ) )
			throw new ArgumentException( "Endpoint URL must include a slug after /endpoints/{projectId}/{slug}, or a slug query parameter." );

		if ( !IsConfigured && !string.IsNullOrWhiteSpace( endpoint.ProjectId ) && !string.IsNullOrWhiteSpace( endpoint.PublicApiKey ) )
		{
			Configure( endpoint.ProjectId, endpoint.PublicApiKey, endpoint.BaseUrl, endpoint.ApiVersion );
			return;
		}

		if ( IsConfigured && !string.IsNullOrWhiteSpace( endpoint.ProjectId ) &&
			!string.Equals( endpoint.ProjectId, ProjectId, StringComparison.Ordinal ) && NetworkStorageLogConfig.LogConfig )
		{
			Log.Warning( "[NetworkStorage] Endpoint URL project id differs from configured project id; using configured credentials." );
		}
	}

	private static Dictionary<string, string> ParseQueryString( string query )
	{
		var result = new Dictionary<string, string>( StringComparer.OrdinalIgnoreCase );
		if ( string.IsNullOrWhiteSpace( query ) ) return result;

		var trimmed = query[0] == '?' ? query[1..] : query;
		foreach ( var pair in trimmed.Split( '&', StringSplitOptions.RemoveEmptyEntries ) )
		{
			var separator = pair.IndexOf( '=' );
			var key = separator >= 0 ? pair[..separator] : pair;
			var value = separator >= 0 ? pair[(separator + 1)..] : "";
			result[Uri.UnescapeDataString( key.Replace( '+', ' ' ) )] = Uri.UnescapeDataString( value.Replace( '+', ' ' ) );
		}

		return result;
	}

	private static string ReadQueryValue( Dictionary<string, string> query, params string[] keys )
	{
		foreach ( var key in keys )
		{
			if ( query.TryGetValue( key, out var value ) && !string.IsNullOrWhiteSpace( value ) )
				return value;
		}

		return null;
	}

	private static string BuildBaseUrlFromEndpointUri( Uri uri, string[] segments, int endpointsIndex, string apiVersion )
	{
		if ( string.IsNullOrWhiteSpace( apiVersion ) || endpointsIndex <= 0 )
			return uri.GetLeftPart( UriPartial.Authority );

		var prefixCount = endpointsIndex - 1;
		if ( prefixCount <= 0 )
			return uri.GetLeftPart( UriPartial.Authority );

		return uri.GetLeftPart( UriPartial.Authority ) + "/" + string.Join( '/', segments[..prefixCount] );
	}

	private static bool IsApiVersionSegment( string segment )
		=> !string.IsNullOrWhiteSpace( segment ) && segment.Length >= 2 && segment[0] == 'v' && char.IsDigit( segment[1] );

	private static string NormalizeSecretKey( string secretKey )
		=> string.IsNullOrWhiteSpace( secretKey ) ? null : secretKey.Trim();

	private static bool _ignoredEndpointUrlSecretLogged;
	private static bool _insecureSecretTransportLogged;
	private static bool _dedicatedSecretTransportLogged;

	private static void LogIgnoredEndpointUrlSecretOnce( EndpointReference endpoint )
	{
		if ( endpoint?.SecretKeyRequested != true || _ignoredEndpointUrlSecretLogged || !NetworkStorageLogConfig.LogConfig )
			return;

		_ignoredEndpointUrlSecretLogged = true;
		Log.Warning( "[NetworkStorage] Ignoring endpoint URL secret key flag because this process is not a dedicated server host." );
	}

	private static void LogDedicatedSecretTransportOnce( string headerName )
	{
		if ( _dedicatedSecretTransportLogged || !NetworkStorageLogConfig.LogConfig ) return;

		_dedicatedSecretTransportLogged = true;
		Log.Info( $"[NetworkStorage] Dedicated server secret key enabled for Network Storage requests (source={DedicatedServerSecretKeySource}, transport=https-header:{headerName}, urlFlag=secret-key=1)." );
	}

	private static void LogInsecureSecretTransportOnce()
	{
		if ( _insecureSecretTransportLogged || !NetworkStorageLogConfig.LogConfig ) return;

		_insecureSecretTransportLogged = true;
		Log.Warning( "[NetworkStorage] Dedicated endpoint secret key was not sent because the API root is not HTTPS." );
	}
}