using System.Net.Http;
using System.Text;

namespace AyCode.Services.Nav;

/// <summary>
/// Általános NAV-adatszolgáltatás base osztály. Egy helyen kezeli a NAV-rendszerekre közös send-flow-t:
/// auth-header feltöltés → XML szerializáció → HTTP POST → válasz deszerializáció → sikeresség-ellenőrzés.
/// A konkrét NAV API (EKÁER, Online Számla, …) ebből származik, és csak a saját namespace-ét / endpoint-ját
/// adja meg a <see cref="OperationPath"/>-ban; a request/response típusait pedig generikus paraméterként.
/// </summary>
/// <remarks>
/// <para><b>Platform-független:</b> a <see cref="HttpClient"/>-et a hívó injektálja (szerver vagy MAUI), így a
/// réteg nem köthető szerverhez. Böngésző-WASM-ből CORS miatt nem hívható közvetlenül — ott a hívás SignalR-en
/// át a szerverre delegálandó.</para>
/// <para><b>Redundancia-mentes:</b> a send-flow + auth-hash itt él egyszer; egy újabb NAV-integráció csak a saját
/// generált modelljeit és a <see cref="OperationPath"/>/<see cref="IsSuccess"/> implementációt adja.</para>
/// </remarks>
/// <typeparam name="TRequest">A NAV request típusa (az API generált root request-je, ami <see cref="INavRequest"/>-et implementál).</typeparam>
/// <typeparam name="TResponse">A NAV response típusa (az API generált root response-a, ami <see cref="INavResponse"/>-t implementál).</typeparam>
public abstract class NavReportServiceBase<TRequest, TResponse>
    where TRequest : class, INavRequest
    where TResponse : class, INavResponse
{
    private readonly HttpClient _httpClient;
    private readonly INavCredentials _credentials;

    protected NavReportServiceBase(HttpClient httpClient, INavCredentials credentials)
    {
        _httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
        _credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
    }

    /// <summary>
    /// A NAV művelet relatív útvonala a <see cref="INavCredentials.BaseUrl"/>-hez képest,
    /// pl. <c>"TradeCardManagementService/customer/manageTradeCards"</c>.
    /// </summary>
    protected abstract string OperationPath { get; }

    /// <summary>
    /// Elküldi a request-et a NAV-nak: feltölti az auth-headert, szerializál, POST-ol, deszerializál, és
    /// ellenőrzi a sikerességet. Hibánál <see cref="NavReportException"/>-t dob.
    /// </summary>
    protected async Task<TResponse> SendAsync(TRequest request, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(request);

        ApplyAuthentication(request);

        var requestXml = NavXmlHelper.Serialize(request);
        var url = $"{_credentials.BaseUrl.TrimEnd('/')}/{OperationPath}";

        // A NAV kötelezően text/xml content-type-ot ÉS accept-et vár (nem application/xml) — PDF §3.3.
        using var content = new StringContent(requestXml, Encoding.UTF8, "text/xml");
        using var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) { Content = content };
        httpRequest.Headers.Accept.ParseAdd("text/xml");

        using var httpResponse = await _httpClient.SendAsync(httpRequest, cancellationToken).ConfigureAwait(false);

        var responseXml = await httpResponse.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);

        if (!httpResponse.IsSuccessStatusCode)
        {
            throw new NavReportException(
                $"NAV HTTP {(int)httpResponse.StatusCode} ({httpResponse.ReasonPhrase}). Body: {Truncate(responseXml, 2000)}",
                httpStatusCode: (int)httpResponse.StatusCode);
        }

        TResponse response;
        try
        {
            response = NavXmlHelper.Deserialize<TResponse>(responseXml);
        }
        catch (Exception ex)
        {
            throw new NavReportException($"NAV válasz deszerializáció sikertelen. Body: {Truncate(responseXml, 2000)}", innerException: ex);
        }

        var result = response.Result;
        if (!result.IsSuccess)
        {
            throw new NavReportException(
                result.Message ?? "NAV adatszolgáltatás sikertelen.",
                reasonCode: result.ReasonCode);
        }

        return response;
    }

    /// <summary>
    /// Feltölti a request base- és auth-headerét: egyedi requestId + timestamp, majd a credentials alapján a
    /// felhasználó/adószám és a két SHA-512 hash (passwordHash, requestSignature). API-független, az
    /// <see cref="INavRequest"/> interfészen keresztül.
    /// </summary>
    private void ApplyAuthentication(TRequest request)
    {
        var requestId = GenerateRequestId();
        // A NAV az aláíráshoz a timestamp UTC-megfelelőjét várja (yyyyMMddHHmmss). UTC-t használunk, így a
        // header timestamp 'Z'-vel (egyértelmű UTC) szerializálódik, és a signature ugyanazzal az értékkel
        // képződik. (eKAERManagementService_2.2.pdf §2.2.3 — lásd Nav/docs/EKAER_INTERFACE.md.)
        var timestamp = DateTime.UtcNow;

        var header = request.RequestHeader;
        header.RequestId = requestId;
        header.Timestamp = timestamp;

        var user = request.UserHeader;
        user.User = _credentials.User;
        user.TaxNumber = _credentials.TaxNumber;
        user.PasswordHash = NavAuthHelper.ComputePasswordHash(_credentials.Password);
        user.RequestSignature = NavAuthHelper.ComputeRequestSignature(requestId, timestamp, _credentials.SigningKey);
    }

    /// <summary>Egyedi requestId. EKÁER <c>BasicHeaderType.requestId</c> pattern: <c>[+a-zA-Z0-9_/=]{1,50}</c>.</summary>
    private static string GenerateRequestId() => Guid.NewGuid().ToString("N");

    private static string Truncate(string value, int max)
        => string.IsNullOrEmpty(value) || value.Length <= max ? value : value[..max] + "…";
}
