<?php
declare(strict_types=1);

const YANDEX_AVATAR_HOST = 'avatars.mds.yandex.net';
const YANDEX_AVATAR_CACHE_SECONDS = 604800;

function fail_avatar_request(int $status): void
{
    http_response_code($status);
    header('Cache-Control: no-store');
    header('X-Content-Type-Options: nosniff');
}

function normalize_yandex_avatar_url(string $url): string
{
    $url = trim(html_entity_decode($url, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
    if (str_starts_with($url, '//')) {
        $url = 'https:' . $url;
    }

    $parts = parse_url($url);
    if (
        !is_array($parts) ||
        ($parts['scheme'] ?? '') !== 'https' ||
        ($parts['host'] ?? '') !== YANDEX_AVATAR_HOST ||
        !str_starts_with($parts['path'] ?? '', '/get-yapic/')
    ) {
        return '';
    }

    return $url;
}

function avatar_cache_path(string $url): string
{
    return __DIR__ . '/cache/yandex-avatars/' . hash('sha256', $url) . '.img';
}

function response_header_value(array $headers, string $name): string
{
    foreach ($headers as $header) {
        if (stripos($header, $name . ':') === 0) {
            return trim(substr($header, strlen($name) + 1));
        }
    }

    return '';
}

function fetch_yandex_avatar(string $url): array
{
    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'timeout' => 8,
            'header' => implode("\r\n", [
                'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125 Safari/537.36',
                'Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
            ]),
        ],
    ]);

    $body = file_get_contents($url, false, $context);
    if ($body === false || $body === '') {
        throw new RuntimeException('Could not load Yandex avatar');
    }

    $contentType = response_header_value($http_response_header ?? [], 'Content-Type');
    if (!preg_match('/^image\/(?:jpeg|png|webp|gif)\b/i', $contentType)) {
        throw new RuntimeException('Yandex avatar response is not an allowed image type');
    }

    return [
        'body' => $body,
        'contentType' => $contentType,
    ];
}

function send_avatar(string $body, string $contentType): void
{
    header('Content-Type: ' . $contentType);
    header('Content-Length: ' . strlen($body));
    header('Cache-Control: public, max-age=' . YANDEX_AVATAR_CACHE_SECONDS . ', immutable');
    header('X-Content-Type-Options: nosniff');

    echo $body;
}

$url = normalize_yandex_avatar_url((string) ($_GET['url'] ?? ''));
if ($url === '') {
    fail_avatar_request(400);
    return;
}

$cachePath = avatar_cache_path($url);
$metaPath = $cachePath . '.json';

if (is_file($cachePath) && is_file($metaPath) && time() - filemtime($cachePath) < YANDEX_AVATAR_CACHE_SECONDS) {
    $meta = json_decode((string) file_get_contents($metaPath), true);
    $contentType = is_array($meta) ? (string) ($meta['contentType'] ?? '') : '';
    if ($contentType !== '') {
        send_avatar((string) file_get_contents($cachePath), $contentType);
        return;
    }
}

try {
    $avatar = fetch_yandex_avatar($url);
} catch (Throwable) {
    fail_avatar_request(502);
    return;
}

$cacheDir = dirname($cachePath);
if (!is_dir($cacheDir)) {
    mkdir($cacheDir, 0755, true);
}
file_put_contents($cachePath, $avatar['body']);
file_put_contents($metaPath, json_encode(['contentType' => $avatar['contentType']], JSON_UNESCAPED_SLASHES));

send_avatar($avatar['body'], $avatar['contentType']);
