#nullable enable
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using Baya.Application.Contracts.Invoices;
using Baya.Domain.Entities.Invoices;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Baya.Infrastructure.CrossCutting.Seams.Real;

/// <summary>
/// Real <see cref="IMoadianClient"/> over سامانه مودیان (refinement-phase-8, 6.5). Selected by
/// <c>Seams:Moadian:Provider = moadian</c>. Submits the commission invoice (صورتحساب) to the tax-authority API and
/// maps the outcome: a returned 22-digit reference ⇒ <see cref="MoadianStatus.Registered"/>; an accepted-but-not-
/// yet-registered submission ⇒ <see cref="MoadianStatus.Submitted"/> (the reconciliation poll walks it to
/// <c>registered</c>); a rejection ⇒ <see cref="MoadianStatus.Failed"/>.
///
/// <para><b>Idempotent submit.</b> The invoice number is sent as the unique document id, so مودیان dedups a
/// re-submission — which is what lets the reconciliation job safely re-call this for a still-<c>submitted</c>
/// invoice (the seam has one verb; a re-submit doubles as the status poll). <b>Enrollment + the signing
/// certificate</b> (memory/economic id, تعهدنامه) are a deploy-time concern — a deployment supplies the memory id
/// + a current token via <c>Seams:Moadian</c>; signing the payload with the platform's private key is added inside
/// this adapter at integration without changing the seam.</para>
/// </summary>
public sealed class MoadianClient(
    HttpClient httpClient,
    IOptions<SeamOptions> options,
    ILogger<MoadianClient> logger) : IMoadianClient
{
    private const string DefaultBaseUrl = "https://tp.tax.gov.ir";
    private readonly MoadianOptions _options = options.Value.Moadian;

    private string BaseUrl => string.IsNullOrWhiteSpace(_options.BaseUrl) ? DefaultBaseUrl : _options.BaseUrl.TrimEnd('/');

    public async ValueTask<MoadianSubmissionResult> SubmitAsync(InvoiceSubmission submission, CancellationToken cancellationToken = default)
    {
        var payload = new
        {
            memoryId = _options.MemoryId,
            invoiceNumber = submission.InvoiceNumber,
            bookingId = submission.BookingId,
            totalAmount = submission.GrossIrr,
            commissionAmount = submission.PlatformCommissionIrr,
            vatAmount = submission.VatIrr,
        };

        using var request = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/req/api/self-tsp/sync/normal-enveloped")
        {
            Content = JsonContent.Create(payload),
        };
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.AccessToken);

        try
        {
            using var response = await httpClient.SendAsync(request, cancellationToken);
            var raw = await response.Content.ReadAsStringAsync(cancellationToken);

            if (!response.IsSuccessStatusCode)
            {
                logger.LogWarning("مودیان submission returned http {Http} for invoice {InvoiceNumber}",
                    (int)response.StatusCode, submission.InvoiceNumber);
                return new MoadianSubmissionResult(MoadianStatus.Failed, null);
            }

            using var doc = JsonDocument.Parse(raw);
            var root = doc.RootElement;

            // A returned reference number registers the invoice; otherwise it was accepted and is awaiting the ref.
            var reference = root.TryGetProperty("referenceNumber", out var refEl) ? refEl.GetString()
                          : root.TryGetProperty("uid", out var uid) ? uid.GetString()
                          : null;

            return string.IsNullOrEmpty(reference)
                ? new MoadianSubmissionResult(MoadianStatus.Submitted, null)
                : new MoadianSubmissionResult(MoadianStatus.Registered, reference);
        }
        catch (Exception ex) when (ex is HttpRequestException or JsonException or TaskCanceledException)
        {
            // A مودیان outage leaves the invoice submittable again on the next reconciliation tick — never failed
            // permanently on a transient error.
            logger.LogWarning(ex, "مودیان submission failed transiently for invoice {InvoiceNumber}", submission.InvoiceNumber);
            return new MoadianSubmissionResult(MoadianStatus.Submitted, null);
        }
    }
}
