using System.Net.Http;
using System.Text;
namespace AyCode.Services.Nav;
///
/// Á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 -ban; a request/response típusait pedig generikus paraméterként.
///
///
/// Platform-független: a -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ó.
/// Redundancia-mentes: a send-flow + auth-hash itt él egyszer; egy újabb NAV-integráció csak a saját
/// generált modelljeit és a / implementációt adja.
///
/// A NAV request típusa (az API generált root request-je, ami -et implementál).
/// A NAV response típusa (az API generált root response-a, ami -t implementál).
public abstract class NavReportServiceBase
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));
}
///
/// A NAV művelet relatív útvonala a -hez képest,
/// pl. "TradeCardManagementService/customer/manageTradeCards".
///
protected abstract string OperationPath { get; }
///
/// 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 -t dob.
///
protected async Task 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(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;
}
///
/// 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
/// interfészen keresztül.
///
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);
}
/// Egyedi requestId. EKÁER BasicHeaderType.requestId pattern: [+a-zA-Z0-9_/=]{1,50}.
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] + "…";
}