using System.Net;
using System.Net.Http.Json;
using System.Linq;
using CashLineERP.Contracts.IslamicFinance;

namespace CashLineERP.Api.Tests;

[Collection("Islamic Finance HTTP QA")]
public sealed class IslamicFinanceSettlementQaTests : IClassFixture<ApiTestFactory>
{
    private readonly HttpClient _client;

    public IslamicFinanceSettlementQaTests(ApiTestFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task Islamic_Finance_Settlement_HTTP_Supports_Create_List_Detail_And_Close()
    {
        await _client.AuthenticateAsInternalAsync();
        var setup = await IslamicFinanceSettlementHttpSeedHelper.SeedAsync(_client, "IFSET-HTTP");
        var settlementNumber = $"IFS-HTTP-{Guid.NewGuid():N}"[..12].ToUpperInvariant();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/islamic-finance/settlements", new CreateIslamicFinanceSettlementRequest
        {
            SettlementNumber = settlementNumber,
            MurabahaCaseId = setup.MurabahaCaseId,
            SettlementDate = new DateOnly(2026, 4, 10),
            SettlementCurrencyCode = "EGP",
            Lines =
            [
                new CreateIslamicFinanceSettlementLineRequest
                {
                    MurabahaDisbursementId = setup.DisbursementId,
                    SettledAmount = setup.ExecutedAmount
                }
            ]
        });

        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var created = await createResponse.Content.ReadFromJsonAsync<IslamicFinanceSettlementResponse>();
        Assert.NotNull(created);

        var listResponse = await _client.GetAsync($"/api/v1/islamic-finance/settlements?murabahaCaseId={setup.MurabahaCaseId}");
        Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
        var list = await listResponse.Content.ReadFromJsonAsync<IslamicFinanceSettlementResponse[]>();
        Assert.NotNull(list);
        Assert.Single(list!);

        var detailResponse = await _client.GetAsync($"/api/v1/islamic-finance/settlements/{created!.Id}");
        Assert.Equal(HttpStatusCode.OK, detailResponse.StatusCode);

        var submitResponse = await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created.Id}/submit", null);
        Assert.Equal(HttpStatusCode.OK, submitResponse.StatusCode);

        await _client.AuthenticateAsFinanceCheckerAsync();

        var approveResponse = await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created.Id}/approve", null);
        Assert.Equal(HttpStatusCode.OK, approveResponse.StatusCode);

        var closeResponse = await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created.Id}/close", null);
        Assert.Equal(HttpStatusCode.OK, closeResponse.StatusCode);
        var closed = await closeResponse.Content.ReadFromJsonAsync<IslamicFinanceSettlementResponse>();
        Assert.NotNull(closed);
        Assert.Equal("CLOSED", closed!.StatusCode);

        var disbursementResponse = await _client.GetAsync($"/api/v1/islamic-finance/disbursements/{setup.DisbursementId}");
        Assert.Equal(HttpStatusCode.OK, disbursementResponse.StatusCode);
        var disbursement = await disbursementResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaDisbursementResponse>();
        Assert.NotNull(disbursement);
        Assert.Equal("CLOSED", disbursement!.StatusCode);
    }

    [Fact]
    public async Task Islamic_Finance_Settlement_History_And_Audit_HTTP_Return_Timeline()
    {
        await _client.AuthenticateAsInternalAsync();
        var setup = await IslamicFinanceSettlementHttpSeedHelper.SeedAsync(_client, "IFSET-HIST");
        var settlementNumber = $"IFS-HIST-{Guid.NewGuid():N}"[..12].ToUpperInvariant();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/islamic-finance/settlements", new CreateIslamicFinanceSettlementRequest
        {
            SettlementNumber = settlementNumber,
            MurabahaCaseId = setup.MurabahaCaseId,
            SettlementDate = new DateOnly(2026, 4, 10),
            SettlementCurrencyCode = "EGP",
            Lines =
            [
                new CreateIslamicFinanceSettlementLineRequest
                {
                    MurabahaDisbursementId = setup.DisbursementId,
                    SettledAmount = setup.ExecutedAmount
                }
            ]
        });
        Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);
        var created = await createResponse.Content.ReadFromJsonAsync<IslamicFinanceSettlementResponse>();
        Assert.NotNull(created);

        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created!.Id}/submit", null)).StatusCode);
        await _client.AuthenticateAsFinanceCheckerAsync();
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created.Id}/approve", null)).StatusCode);
        Assert.Equal(HttpStatusCode.OK, (await _client.PostAsync($"/api/v1/islamic-finance/settlements/{created.Id}/close", null)).StatusCode);

        var historyResponse = await _client.GetAsync($"/api/v1/islamic-finance/settlements/{created.Id}/history");
        Assert.Equal(HttpStatusCode.OK, historyResponse.StatusCode);
        var history = await historyResponse.Content.ReadFromJsonAsync<IslamicFinanceLifecycleHistoryEntryResponse[]>();
        Assert.NotNull(history);
        Assert.Contains(history!, entry => entry.EventCode == "SETTLEMENT_CREATED");
        Assert.Contains(history!, entry => entry.EventCode == "SETTLEMENT_LINE_LINKED");
        Assert.Contains(history!, entry => entry.EventCode == "SETTLEMENT_CLOSED");
        Assert.True(history!.SequenceEqual(history.OrderBy(entry => entry.OccurredAtUtc)));

        var auditResponse = await _client.GetAsync($"/api/v1/islamic-finance/settlements/{created.Id}/audit");
        Assert.Equal(HttpStatusCode.OK, auditResponse.StatusCode);
        var audit = await auditResponse.Content.ReadFromJsonAsync<IslamicFinanceSettlementAuditResponse>();
        Assert.NotNull(audit);
        Assert.Equal(created.Id, audit!.Settlement.Id);
        Assert.Contains(audit.Events, entry => entry.EventCode == "SETTLEMENT_CLOSED");
    }
}

internal static class IslamicFinanceSettlementHttpSeedHelper
{
    public static async Task<(long MurabahaCaseId, long DisbursementId, decimal ExecutedAmount)> SeedAsync(HttpClient client, string prefix)
    {
        var token = Guid.NewGuid().ToString("N")[..6].ToUpperInvariant();
        var runPrefix = $"{prefix}-{token}";

        static async Task<T> ReadOkAsync<T>(HttpResponseMessage response)
        {
            if (response.StatusCode != HttpStatusCode.OK)
            {
                throw new InvalidOperationException(await response.Content.ReadAsStringAsync());
            }

            return await response.Content.ReadFromJsonAsync<T>() ?? throw new InvalidOperationException();
        }

        static async Task EnsureOkAsync(HttpResponseMessage response)
        {
            if (response.StatusCode != HttpStatusCode.OK)
            {
                throw new InvalidOperationException(await response.Content.ReadAsStringAsync());
            }
        }

        await client.AuthenticateAsInternalAsync();
        var activePartners = await ReadOkAsync<IslamicFinancePartnerLenderResponse[]>(await client.GetAsync("/api/v1/islamic-finance/partners?activeOnly=true"));
        IslamicFinancePartnerLenderResponse activatedPartner;
        var existingPartner = activePartners.FirstOrDefault(entry => entry.PartnerLenderPartyId == 1);
        if (existingPartner is not null)
        {
            activatedPartner = existingPartner;
        }
        else
        {
            var partner = await ReadOkAsync<IslamicFinancePartnerLenderResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/partners", new CreateIslamicFinancePartnerLenderRequest
            {
                PartnerLenderPartyId = 1,
                DefaultCurrencyCode = "EGP",
                SupportsMurabaha = true,
                SupportsReceivablesFinance = false,
                SupportsTawarruq = false
            }));
            await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/partners/{partner.Id}/submit", null));
            await client.AuthenticateAsFinanceCheckerAsync();
            await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/partners/{partner.Id}/approve", null));
            activatedPartner = await ReadOkAsync<IslamicFinancePartnerLenderResponse>(await client.PostAsync($"/api/v1/islamic-finance/partners/{partner.Id}/activate", null));
            await client.AuthenticateAsInternalAsync();
        }

        await client.AuthenticateAsInternalAsync();
        var product = await ReadOkAsync<IslamicFinanceProductProfileResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/product-profiles", new CreateIslamicFinanceProductProfileRequest
        {
            ProductType = "MURABAHA",
            ProductCode = $"{runPrefix}-PROD",
            ProductName = $"{runPrefix} Product",
            RequiresShariahReview = true,
            AllowsPartnerFunding = true
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/product-profiles/{product.Id}/submit", null));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/product-profiles/{product.Id}/approve", null));
        var activatedProduct = await ReadOkAsync<IslamicFinanceProductProfileResponse>(await client.PostAsync($"/api/v1/islamic-finance/product-profiles/{product.Id}/activate", null));

        await client.AuthenticateAsInternalAsync();
        var template = await ReadOkAsync<IslamicFinanceContractTemplateResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/contract-templates", new CreateIslamicFinanceContractTemplateRequest
        {
            ProductProfileId = activatedProduct.Id,
            TemplateCode = $"{runPrefix}-TPL",
            TemplateName = $"{runPrefix} Template",
            LanguageCode = "en"
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/contract-templates/{template.Id}/submit", null));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/contract-templates/{template.Id}/approve", null));
        var activatedTemplate = await ReadOkAsync<IslamicFinanceContractTemplateResponse>(await client.PostAsync($"/api/v1/islamic-finance/contract-templates/{template.Id}/activate", null));

        await client.AuthenticateAsInternalAsync();
        var facility = await ReadOkAsync<IslamicFinanceFacilityResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/facilities", new CreateIslamicFinanceFacilityRequest
        {
            PartnerLenderId = activatedPartner.Id,
            CustomerPartyId = 1,
            ProductProfileId = activatedProduct.Id,
            FacilityNumber = $"{runPrefix}-FAC",
            CurrencyCode = "EGP",
            ApprovedLimitAmount = 250000m
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/facilities/{facility.Id}/submit", null));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/facilities/{facility.Id}/approve", null));
        var activatedFacility = await ReadOkAsync<IslamicFinanceFacilityResponse>(await client.PostAsync($"/api/v1/islamic-finance/facilities/{facility.Id}/activate", null));

        await client.AuthenticateAsInternalAsync();
        var request = await ReadOkAsync<IslamicFinanceFinancingRequestResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/requests", new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = $"{runPrefix}-REQ",
            CustomerPartyId = 1,
            ProductProfileId = activatedProduct.Id,
            FacilityId = activatedFacility.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 200100 + (Math.Abs(token.GetHashCode()) % 100000),
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 100000m
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/requests/{request.Id}/submit", null));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/requests/{request.Id}/approve", null));
        await client.AuthenticateAsInternalAsync();
        await EnsureOkAsync(await client.PostAsJsonAsync($"/api/v1/islamic-finance/requests/{request.Id}/assign-partner-lender", new AssignIslamicFinancePartnerLenderRequest { PartnerLenderId = activatedPartner.Id }));

        var review = await ReadOkAsync<IslamicFinanceShariahReviewResponse>(await client.PostAsJsonAsync("/api/v1/islamic-finance/shariah-reviews", new CreateIslamicFinanceShariahReviewRequest
        {
            ReviewCaseNumber = $"{runPrefix}-SHR",
            ProductProfileId = activatedProduct.Id,
            FinancingRequestId = request.Id,
            ReviewReasonCode = "SHARIAH_REVIEW"
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/shariah-reviews/{review.Id}/submit", null));
        await EnsureOkAsync(await client.PostAsJsonAsync($"/api/v1/islamic-finance/shariah-reviews/{review.Id}/recommend-outcome", new RecommendIslamicFinanceShariahOutcomeRequest
        {
            ReviewOutcomeCode = "APPROVED",
            ReviewNotes = "Approved for settlement HTTP QA."
        }));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/shariah-reviews/{review.Id}/approve-outcome", null));
        await client.AuthenticateAsInternalAsync();
        await EnsureOkAsync(await client.PostAsJsonAsync($"/api/v1/islamic-finance/requests/{request.Id}/attach-shariah-review", new AttachIslamicFinanceShariahReviewRequest { ShariahReviewCaseId = review.Id }));

        var decision = await ReadOkAsync<IslamicFinanceDecisionResponse>(await client.PostAsJsonAsync($"/api/v1/islamic-finance/requests/{request.Id}/decisions", new CreateIslamicFinanceDecisionRequest
        {
            DecisionCode = "APPROVED",
            PartnerLenderId = activatedPartner.Id
        }));
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/decisions/{decision.Id}/submit", null));
        await client.AuthenticateAsFinanceCheckerAsync();
        await EnsureOkAsync(await client.PostAsync($"/api/v1/islamic-finance/decisions/{decision.Id}/approve", null));

        await client.AuthenticateAsInternalAsync();
        var murabahaCaseCreateResponse = await client.PostAsJsonAsync("/api/v1/islamic-finance/murabaha-cases", new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = $"{runPrefix}-CASE",
            FinancingRequestId = request.Id,
            CustomerPartyId = 1,
            PartnerLenderId = activatedPartner.Id,
            SupplierPartyId = 2,
            ProductProfileId = activatedProduct.Id,
            ContractTemplateId = activatedTemplate.Id,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        });
        if (murabahaCaseCreateResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await murabahaCaseCreateResponse.Content.ReadAsStringAsync());
        }

        var murabahaCase = await murabahaCaseCreateResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaCaseResponse>() ?? throw new InvalidOperationException();
        await client.PostAsync($"/api/v1/islamic-finance/murabaha-cases/{murabahaCase.Id}/submit", null);
        await client.AuthenticateAsFinanceCheckerAsync();
        await client.PostAsync($"/api/v1/islamic-finance/murabaha-cases/{murabahaCase.Id}/approve", null);
        var activateResponse = await client.PostAsync($"/api/v1/islamic-finance/murabaha-cases/{murabahaCase.Id}/activate", null);
        if (activateResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await activateResponse.Content.ReadAsStringAsync());
        }
        await client.AuthenticateAsInternalAsync();
        var contractResponse = await client.PostAsJsonAsync($"/api/v1/islamic-finance/murabaha-cases/{murabahaCase.Id}/generate-contract", new GenerateIslamicFinanceMurabahaContractRequest());
        if (contractResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await contractResponse.Content.ReadAsStringAsync());
        }

        var scheduleResponse = await client.PostAsJsonAsync($"/api/v1/islamic-finance/murabaha-cases/{murabahaCase.Id}/generate-schedule", new GenerateIslamicFinanceMurabahaScheduleRequest
        {
            InstallmentCount = 2,
            FirstDueDate = new DateOnly(2026, 5, 1)
        });
        if (scheduleResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await scheduleResponse.Content.ReadAsStringAsync());
        }

        var disbursementCreateResponse = await client.PostAsJsonAsync("/api/v1/islamic-finance/disbursements", new CreateIslamicFinanceMurabahaDisbursementRequest
        {
            MurabahaCaseId = murabahaCase.Id,
            DisbursementNumber = $"{runPrefix}-DISB",
            TargetType = "SUPPLIER_PAYMENT",
            InstructedAmount = 60000m,
            PlannedExecutionDate = new DateOnly(2026, 4, 7)
        });
        if (disbursementCreateResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await disbursementCreateResponse.Content.ReadAsStringAsync());
        }

        var disbursement = await disbursementCreateResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaDisbursementResponse>() ?? throw new InvalidOperationException();
        await client.PostAsync($"/api/v1/islamic-finance/disbursements/{disbursement.Id}/submit", null);
        await client.AuthenticateAsFinanceCheckerAsync();
        await client.PostAsync($"/api/v1/islamic-finance/disbursements/{disbursement.Id}/approve", null);
        await client.AuthenticateAsInternalAsync();
        var executeResponse = await client.PostAsJsonAsync($"/api/v1/islamic-finance/disbursements/{disbursement.Id}/execute", new ExecuteIslamicFinanceMurabahaDisbursementRequest
        {
            ExecutionReferenceNumber = $"{runPrefix}-PAY"
        });
        if (executeResponse.StatusCode != HttpStatusCode.OK)
        {
            throw new InvalidOperationException(await executeResponse.Content.ReadAsStringAsync());
        }

        var executed = await executeResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaDisbursementResponse>() ?? throw new InvalidOperationException();

        return (murabahaCase.Id, executed.Id, executed.ExecutedAmount);
    }
}
