<?php

declare(strict_types=1);

namespace Drupal\ffmpeg_media\Cdn;

/**
 * Validates S3-compatible endpoint URLs for public HTTPS use.
 *
 * @internal Validation helper for FFmpeg Media CDN integrations.
 */
final class EndpointValidator {

  /**
   * Invalid endpoint URL structure.
   */
  public const string ERROR_INVALID_FORMAT = 'invalid_format';

  /**
   * Endpoint does not use HTTPS.
   */
  public const string ERROR_INSECURE_SCHEME = 'insecure_scheme';

  /**
   * Endpoint host resolves to a non-public address literal.
   */
  public const string ERROR_NON_PUBLIC_HOST = 'non_public_host';

  /**
   * Validates an endpoint URL for public HTTPS usage.
   *
   * @param string $endpoint
   *   Endpoint URL to validate.
   *
   * @return string|null
   *   NULL when valid, otherwise one of the ERROR_* constants.
   */
  public static function validatePublicHttpsEndpoint(string $endpoint): ?string {
    $parsed = parse_url($endpoint);
    if ($parsed === FALSE || !isset($parsed['host'])) {
      return self::ERROR_INVALID_FORMAT;
    }

    if (strtolower((string) ($parsed['scheme'] ?? '')) !== 'https') {
      return self::ERROR_INSECURE_SCHEME;
    }

    $host = trim(strtolower((string) $parsed['host']), '[]');
    if (self::isNonPublicHostLiteral($host)) {
      return self::ERROR_NON_PUBLIC_HOST;
    }

    return NULL;
  }

  /**
   * Determines whether a host is a disallowed non-public address literal.
   *
   * Host names are accepted; only localhost and private/reserved IP literals
   * are rejected here. This avoids network-dependent DNS resolution during
   * validation while still blocking the most obvious SSRF-prone inputs.
   *
   * @param string $host
   *   Host name or IP literal.
   *
   * @return bool
   *   TRUE when the host is non-public and should be rejected.
   */
  private static function isNonPublicHostLiteral(string $host): bool {
    if ($host === 'localhost' || str_ends_with($host, '.localhost')) {
      return TRUE;
    }

    if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE) {
      return filter_var(
        $host,
        FILTER_VALIDATE_IP,
        FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE,
      ) === FALSE;
    }

    if (str_starts_with($host, '::ffff:')) {
      $mappedIpv4 = substr($host, 7);
      if (filter_var($mappedIpv4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== FALSE) {
        return self::isNonPublicHostLiteral($mappedIpv4);
      }
    }

    if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === FALSE) {
      return FALSE;
    }

    if ($host === '::' || $host === '::1') {
      return TRUE;
    }

    $packed = inet_pton($host);
    if ($packed === FALSE) {
      return TRUE;
    }

    $firstByte = ord($packed[0]);
    $secondByte = ord($packed[1] ?? "\0");

    // Unique local addresses fc00::/7.
    if (($firstByte & 0xfe) === 0xfc) {
      return TRUE;
    }

    // Link-local addresses fe80::/10.
    if ($firstByte === 0xfe && ($secondByte & 0xc0) === 0x80) {
      return TRUE;
    }

    return FALSE;
  }

}
