using System.Net.Http.Json;
using System.Reflection;
using System.Runtime.InteropServices;

namespace Zcbot.WindowsNode;

internal static class EnrollmentClient
{
    internal static async Task EnrollAsync(
        EnrollOptions options, NodeConfigStore store, CancellationToken cancellationToken)
    {
        if (store.Exists)
        {
            throw new NodeConfigurationException(
                $"Node is already registered. Remove {store.ConfigPath} through the administrator recovery procedure before re-enrolling.");
        }

        var installId = Guid.NewGuid();
        var request = new EnrollRequest(
            options.EnrollmentCode,
            options.NodeName,
            installId,
            Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "0.1.0",
            RuntimeInformation.OSDescription,
            NodeAdapterRegistry.InstalledCapabilities);

        using var client = new HttpClient { BaseAddress = options.ServerUrl, Timeout = TimeSpan.FromSeconds(30) };
        using var response = await client.PostAsJsonAsync(
            "v1/software-nodes/enroll", request, cancellationToken);
        if (!response.IsSuccessStatusCode)
        {
            var detail = await response.Content.ReadAsStringAsync(cancellationToken);
            throw new NodeConfigurationException(
                $"Registration failed with HTTP {(int)response.StatusCode}: {Limit(detail, 300)}");
        }
        var enrolled = await response.Content.ReadFromJsonAsync<EnrollResponse>(cancellationToken)
            ?? throw new NodeConfigurationException("Registration response was empty.");
        if (enrolled.NodeId == Guid.Empty || string.IsNullOrWhiteSpace(enrolled.NodeToken))
        {
            throw new NodeConfigurationException("Registration response did not contain a valid node identity.");
        }

        store.Save(new NodeConfig(
            options.ServerUrl, enrolled.NodeId, installId, options.NodeName,
            enrolled.NodeToken, Math.Clamp(enrolled.HeartbeatSeconds, 5, 300),
            NodeAdapterRegistry.InstalledCapabilities));
    }

    private static string Limit(string value, int maxLength) =>
        value.Length <= maxLength ? value : value[..maxLength];
}
