using System.Net.Http.Headers;
using System.Net.Http.Json;
using Lumi.Companion.Abstractions;
using Lumi.Companion.Protocol;

namespace Lumi.Companion.Core;

public sealed class CompanionPluginTransport(
    HttpClient http,
    Func<DeviceCredential?> credentialProvider,
    string pluginId) : ICompanionPluginTransport
{
    private readonly string _pluginId = ValidatePluginId(pluginId);

    public Uri? LumiBaseUri
    {
        get
        {
            try
            {
                var credential = credentialProvider();
                return credential is null ? null : new Uri(credential.Host);
            }
            catch
            {
                return null;
            }
        }
    }

    public bool IsAuthenticated => LumiBaseUri is not null;

    public async Task<CompanionPluginHttpResponse> PostJsonAsync(
        string relativePath,
        object payload,
        CancellationToken cancellationToken = default)
    {
        var credential = credentialProvider()
            ?? throw new InvalidOperationException("Pair Companion with Lumi before using this plugin.");
        var host = new Uri(credential.Host);
        if (!CompanionTransportPolicy.IsPairedHostAllowed(host))
            throw new InvalidDataException("Companion transport requires HTTPS unless this device was paired with localhost.");

        var expectedPrefix = $"/plugins/{_pluginId}/";
        if (!relativePath.StartsWith(expectedPrefix, StringComparison.Ordinal) ||
            !Uri.TryCreate(relativePath, UriKind.Relative, out var relative))
            throw new InvalidDataException($"Plugin '{_pluginId}' attempted to use an endpoint outside its Lumi plugin scope.");

        var endpoint = new Uri(host, relative);
        if (!CompanionTransportPolicy.IsEndpointAllowed(endpoint, host))
            throw new InvalidDataException("The plugin endpoint does not match the paired Lumi host.");

        using var request = new HttpRequestMessage(HttpMethod.Post, endpoint)
        {
            Content = JsonContent.Create(payload, options: ProtocolV1.JsonOptions)
        };
        request.Headers.Authorization = new AuthenticationHeaderValue(
            "LumiDevice",
            $"{credential.DeviceId}.{credential.DeviceSecret}");
        request.Headers.UserAgent.ParseAdd("Lumi-Companion/1.0");
        request.Headers.Add("X-Lumi-Companion-Plugin", _pluginId);

        using var response = await http.SendAsync(request, cancellationToken).ConfigureAwait(false);
        var body = await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
        return new CompanionPluginHttpResponse((int)response.StatusCode, body);
    }

    private static string ValidatePluginId(string value)
    {
        var clean = (value ?? string.Empty).Trim();
        if (clean.Length is < 1 or > 100 ||
            clean.Any(character => !char.IsAsciiLetterOrDigit(character) && character is not '_' and not '-' and not '.'))
            throw new ArgumentException("A valid Companion plugin identifier is required.", nameof(value));
        return clean;
    }
}
