namespace Sunfish.Web;

/// <summary>Tipo de cliente; decide formato de pagina, juego de caracteres y nombres (ver DOC/CLIENTES.md).</summary>
public enum ClientKind { Modern, Classic, Text, Dos, Win16, Mac }

public sealed record ClientProfile(ClientKind Kind, bool Forced)
{
    /// <summary>Pagina en modo moderno (UTF-8, CSS); el resto usa el modo clasico.</summary>
    public bool IsModern => Kind == ClientKind.Modern;

    /// <summary>Navegador de texto: listados como lineas, sin depender de tablas ni imagenes.</summary>
    public bool IsText => Kind == ClientKind.Text || Kind == ClientKind.Dos;

    /// <summary>DOS y Windows 3.1: solo entienden nombres 8.3 (se les muestran alias).</summary>
    public bool UsesShortNames => Kind is ClientKind.Dos or ClientKind.Win16;

    /// <summary>Valor para ?p= (se propaga en los enlaces cuando el perfil se forzo).</summary>
    public string Code => Kind switch
    {
        ClientKind.Modern => "modern",
        ClientKind.Text => "text",
        ClientKind.Dos => "dos",
        ClientKind.Win16 => "win16",
        ClientKind.Mac => "mac",
        _ => "classic",
    };

    public static bool TryParseCode(string? code, out ClientKind kind)
    {
        kind = ClientKind.Classic;
        switch (code?.ToLowerInvariant())
        {
            case "modern": kind = ClientKind.Modern; return true;
            case "classic": kind = ClientKind.Classic; return true;
            case "text": kind = ClientKind.Text; return true;
            case "dos": kind = ClientKind.Dos; return true;
            case "win16": kind = ClientKind.Win16; return true;
            case "mac": kind = ClientKind.Mac; return true;
            default: return false;
        }
    }

    /// <summary>
    /// Perfil por ?p= (forzado) o por el User-Agent. La lista de firmas es orientativa y se
    /// amplia al probar con clientes reales; lo dudoso cae en Classic, que funciona en todos.
    /// </summary>
    public static ClientProfile Detect(string? userAgent, string? forced)
    {
        if (TryParseCode(forced, out var kind))
            return new ClientProfile(kind, true);
        return new ClientProfile(FromUserAgent(userAgent), false);
    }

    internal static ClientKind FromUserAgent(string? ua)
    {
        if (string.IsNullOrWhiteSpace(ua))
            return ClientKind.Classic; // herramientas HTTP/1.0 y clientes que no se identifican

        if (Has(ua, "DOS") || Has(ua, "Arachne") || Has(ua, "DOSLynx"))
            return ClientKind.Dos;
        if (Has(ua, "Lynx") || Has(ua, "Links") || Has(ua, "w3m"))
            return ClientKind.Text;
        if (Has(ua, "Win16") || Has(ua, "Windows 3.1") || Has(ua, "Windows 3.11"))
            return ClientKind.Win16;
        if (IsClassicMac(ua))
            return ClientKind.Mac;
        if (IsModernAgent(ua))
            return ClientKind.Modern;
        return ClientKind.Classic;
    }

    private static bool IsClassicMac(string ua)
    {
        if (Has(ua, "Mac OS X") || Has(ua, "Intel Mac") || Has(ua, "iPhone") || Has(ua, "iPad"))
            return false;
        return Has(ua, "Mac_PowerPC") || Has(ua, "Mac_PPC") || Has(ua, "Mac_68000") || Has(ua, "Mac_68K") ||
               Has(ua, "MacWeb") || (Has(ua, "Macintosh") && (Has(ua, "PPC") || Has(ua, "68K")));
    }

    private static bool IsModernAgent(string ua) =>
        ua.StartsWith("Mozilla/5.0", StringComparison.Ordinal) &&
        (Has(ua, "AppleWebKit/") || Has(ua, "Gecko/") || Has(ua, "Trident/7"));

    private static bool Has(string ua, string token) => ua.Contains(token, StringComparison.OrdinalIgnoreCase);
}
