<?php

namespace App\Service\Security;

/**
 * Identify and allow internal requests between api v1 and v2
 *
 * @see doc/internal_requests.md
 */
class InternalRequestsService
{
    // Internal ips allowed to access private files without being authenticated
    // TODO: voir à déplacer cette array dans la conf ou dans le .env?
    const INTERNAL_IPS = [
        '/^127\.0\.0\.[0-1]$/', // Localhost
        '/^localhost$/',  // Localhost
        '/^10\.8\.0\.\d{1,3}$/', // 10.8.0.[0-255] - VPN
        '/^141\.94\.117\.((3[3-9])|(4\d)|(5\d)|(6[0-1]))$/',   // 141.94.117.[33-61] - Opentalent hosts public ips
        '/^172\.16\.0.\d{1,3}$/',   // 172.16.0.[0-255] - Opentalent hosts private ips
        '/^172\.20\.\d{1,3}\.\d{1,3}$/',  // 172.20.[0-255].[0-255] - Docker
    ];

    public function __construct(
        readonly private string $internalRequestsToken
    ) {}

    /**
     * Returns true if the client Ip is allowed to access restricted content without auth
     *
     * @param string $clientIp
     * @return bool
     */
    protected function isInternalIp(string $clientIp): bool
    {
        foreach (self::INTERNAL_IPS as $ipRule) {
            if (preg_match($ipRule, $clientIp)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Compare the given token to the expected one, and return true if they are identical
     * An empty token can not be valid
     *
     * @param string $token
     * @return bool
     */
    protected function tokenIsValid(string $token): bool {
        return $token && $token === $this->internalRequestsToken;
    }

    /**
     * Is the given request a valid internal request, which shall be responded even without authentication
     *
     * @param string $ip
     * @param string $token
     * @return bool
     */
    public function isAllowed(string $ip, string $token): bool {
        return $this->isInternalIp($ip) && $this->tokenIsValid($token);
    }
}
