<?php

namespace App\Services\Devices;

/**
 * "Chrome auf macOS", from a user-agent string.
 *
 * For the person reading their list of sessions, and for nothing else. It never
 * decides whether a device is known — which is exactly why it is allowed to be
 * this rough, and why it is recomputed on every sign-in: a browser upgrade
 * quietly relabels the row instead of raising an alarm.
 *
 * Order matters throughout. Every Chromium browser also says "Chrome", Edge
 * also says "Chrome" and "Safari", and Safari says "Safari" while Chrome says
 * it too — so the specific names have to be tested before the generic ones.
 */
final class DeviceName
{
    /** @var array<string, string> needle => label, most specific first */
    private const BROWSERS = [
        'Edg/' => 'Edge',
        'OPR/' => 'Opera',
        'Vivaldi' => 'Vivaldi',
        'Brave' => 'Brave',
        'SamsungBrowser' => 'Samsung Internet',
        'Firefox' => 'Firefox',
        'CriOS' => 'Chrome',       // Chrome on iOS, which is Safari underneath
        'FxiOS' => 'Firefox',
        'Chrome' => 'Chrome',
        'Safari' => 'Safari',
    ];

    /** @var array<string, string> needle => label, most specific first */
    private const PLATFORMS = [
        'iPhone' => 'iPhone',
        'iPad' => 'iPad',
        'Android' => 'Android',
        'Macintosh' => 'macOS',
        'Mac OS X' => 'macOS',
        'Windows' => 'Windows',
        'CrOS' => 'ChromeOS',
        'Linux' => 'Linux',
    ];

    public static function from(string $userAgent): string
    {
        $browser = self::match($userAgent, self::BROWSERS);
        $platform = self::match($userAgent, self::PLATFORMS);

        if ($browser !== null && $platform !== null) {
            return __('devices.name_on', ['browser' => $browser, 'platform' => $platform]);
        }

        // One of them is better than the shrug, and the shrug is better than a
        // raw user-agent string in a list somebody is meant to recognise
        // themselves in.
        return $browser ?? $platform ?? __('devices.name_unknown');
    }

    /** @param array<string, string> $map */
    private static function match(string $haystack, array $map): ?string
    {
        foreach ($map as $needle => $label) {
            if (str_contains($haystack, $needle)) {
                return $label;
            }
        }

        return null;
    }
}
