using Microsoft.AspNetCore.Http;
using Ikar.Server.Data.Entities;
using Ikar.Shared;

namespace Ikar.Server.Infrastructure.Auth;

public sealed record ClientVersionSnapshot(
    string? ClientPlatform,
    int? ClientVersionCode,
    string? ClientVersionName);

public static class ClientVersionRequestReader
{
    private const int MaxClientPlatformLength = 32;
    private const int MaxClientVersionNameLength = 64;

    public static ClientVersionSnapshot Read(HttpRequest request)
    {
        var clientPlatform = TrimToNull(
            ReadString(request, ClientVersionMetadata.PlatformHeaderName, ClientVersionMetadata.PlatformQueryName),
            MaxClientPlatformLength);

        var clientVersionName = TrimToNull(
            ReadString(request, ClientVersionMetadata.VersionNameHeaderName, ClientVersionMetadata.VersionNameQueryName),
            MaxClientVersionNameLength);

        var clientVersionCode = TryGetClientVersionCode(request, out var parsedVersionCode)
            ? parsedVersionCode
            : (int?)null;

        return new ClientVersionSnapshot(clientPlatform, clientVersionCode, clientVersionName);
    }

    public static bool Apply(UserSession session, ClientVersionSnapshot snapshot)
    {
        return Apply(
            snapshot,
            session.ClientPlatform,
            session.ClientVersionCode,
            session.ClientVersionName,
            platform => session.ClientPlatform = platform,
            versionCode => session.ClientVersionCode = versionCode,
            versionName => session.ClientVersionName = versionName);
    }

    public static bool Apply(PushDevice device, ClientVersionSnapshot snapshot)
    {
        return Apply(
            snapshot,
            device.ClientPlatform,
            device.ClientVersionCode,
            device.ClientVersionName,
            platform => device.ClientPlatform = platform,
            versionCode => device.ClientVersionCode = versionCode,
            versionName => device.ClientVersionName = versionName);
    }

    private static bool Apply(
        ClientVersionSnapshot snapshot,
        string? currentPlatform,
        int? currentVersionCode,
        string? currentVersionName,
        Action<string?> setPlatform,
        Action<int?> setVersionCode,
        Action<string?> setVersionName)
    {
        var changed = false;

        if (snapshot.ClientPlatform is not null &&
            !string.Equals(currentPlatform, snapshot.ClientPlatform, StringComparison.Ordinal))
        {
            setPlatform(snapshot.ClientPlatform);
            changed = true;
        }

        if (snapshot.ClientVersionCode is not null && currentVersionCode != snapshot.ClientVersionCode)
        {
            setVersionCode(snapshot.ClientVersionCode);
            changed = true;
        }

        if (snapshot.ClientVersionName is not null &&
            !string.Equals(currentVersionName, snapshot.ClientVersionName, StringComparison.Ordinal))
        {
            setVersionName(snapshot.ClientVersionName);
            changed = true;
        }

        return changed;
    }

    public static bool TryGetClientPlatform(HttpRequest request, out string? clientPlatform)
    {
        clientPlatform = TrimToNull(
            ReadString(request, ClientVersionMetadata.PlatformHeaderName, ClientVersionMetadata.PlatformQueryName),
            MaxClientPlatformLength);
        return clientPlatform is not null;
    }

    public static bool TryGetClientVersionCode(HttpRequest request, out int clientVersionCode)
    {
        clientVersionCode = 0;
        var rawVersionCode = ReadString(
            request,
            ClientVersionMetadata.VersionCodeHeaderName,
            ClientVersionMetadata.VersionCodeQueryName);

        return int.TryParse(rawVersionCode, out clientVersionCode) && clientVersionCode > 0;
    }

    private static string? ReadString(HttpRequest request, string headerName, string queryName)
    {
        var value = request.Headers[headerName].ToString();
        if (!string.IsNullOrWhiteSpace(value))
        {
            return value;
        }

        value = request.Query[queryName].ToString();
        return string.IsNullOrWhiteSpace(value) ? null : value;
    }

    private static string? TrimToNull(string? value, int maxLength)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            return null;
        }

        var trimmed = value.Trim();
        return trimmed.Length <= maxLength ? trimmed : trimmed[..maxLength];
    }
}
