using System.Net;
using System.Net.Http.Json;
using System.Linq;
using CashLineERP.Application.IslamicFinance.Facilities;
using CashLineERP.Application.IslamicFinance.PartnerLenders;
using CashLineERP.Application.IslamicFinance.ProductProfiles;
using CashLineERP.Application.IslamicFinance.Requests;
using CashLineERP.Application.IslamicFinance.Shariah;
using CashLineERP.Contracts.IslamicFinance;
using CashLineERP.Infrastructure.Persistence;
using CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;
using Microsoft.Extensions.DependencyInjection;

namespace CashLineERP.Api.Tests;

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

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

    [Fact]
    public async Task Murabaha_Case_Endpoints_Support_Create_Submit_Approve_Activate_And_Reads()
    {
        await _client.AuthenticateAsInternalAsync();
        var seeded = SeedApprovedAssignedMurabahaPrerequisites();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/islamic-finance/murabaha-cases", new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = $"MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            FinancingRequestId = seeded.RequestId,
            CustomerPartyId = 1,
            PartnerLenderId = seeded.PartnerId,
            SupplierPartyId = 2,
            ProductProfileId = seeded.ProductId,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        });

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

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

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

        var activateResponse = await _client.PostAsync($"/api/v1/islamic-finance/murabaha-cases/{created.Id}/activate", null);
        Assert.Equal(HttpStatusCode.OK, activateResponse.StatusCode);
        var activated = await activateResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaCaseResponse>();
        Assert.NotNull(activated);
        Assert.Equal("ACTIVE", activated!.StatusCode);

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

        var listResponse = await _client.GetAsync($"/api/v1/islamic-finance/murabaha-cases?statusCode=ACTIVE&financingRequestId={seeded.RequestId}&partnerLenderId={seeded.PartnerId}");
        Assert.Equal(HttpStatusCode.OK, listResponse.StatusCode);
        var list = await listResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaCaseResponse[]>();
        Assert.NotNull(list);
        Assert.Single(list!);
    }

    [Fact]
    public async Task Murabaha_Case_Create_Blocks_For_Unassigned_Request()
    {
        await _client.AuthenticateAsInternalAsync();
        var seeded = SeedUnassignedMurabahaPrerequisites();

        var createResponse = await _client.PostAsJsonAsync("/api/v1/islamic-finance/murabaha-cases", new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = $"MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            FinancingRequestId = seeded.RequestId,
            CustomerPartyId = 1,
            PartnerLenderId = seeded.PartnerId,
            SupplierPartyId = 2,
            ProductProfileId = seeded.ProductId,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        });

        Assert.Equal(HttpStatusCode.BadRequest, createResponse.StatusCode);
    }

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

        var settlementResponse = 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, settlementResponse.StatusCode);

        var historyResponse = await _client.GetAsync($"/api/v1/islamic-finance/murabaha-cases/{setup.MurabahaCaseId}/history");
        Assert.Equal(HttpStatusCode.OK, historyResponse.StatusCode);
        var history = await historyResponse.Content.ReadFromJsonAsync<IslamicFinanceLifecycleHistoryEntryResponse[]>();
        Assert.NotNull(history);
        Assert.Contains(history!, entry => entry.EventCode == "MURABAHA_CASE_CREATED");
        Assert.Contains(history!, entry => entry.EventCode == "MURABAHA_CONTRACT_GENERATED");
        Assert.Contains(history!, entry => entry.EventCode == "MURABAHA_SCHEDULE_GENERATED");
        Assert.Contains(history!, entry => entry.EventCode == "DISBURSEMENT_EXECUTED");
        Assert.Contains(history!, entry => entry.EventCode == "SETTLEMENT_CREATED");
        Assert.True(history!.SequenceEqual(history.OrderBy(entry => entry.OccurredAtUtc)));

        var auditResponse = await _client.GetAsync($"/api/v1/islamic-finance/murabaha-cases/{setup.MurabahaCaseId}/audit");
        Assert.Equal(HttpStatusCode.OK, auditResponse.StatusCode);
        var audit = await auditResponse.Content.ReadFromJsonAsync<IslamicFinanceMurabahaCaseAuditResponse>();
        Assert.NotNull(audit);
        Assert.Equal(setup.MurabahaCaseId, audit!.MurabahaCase.Id);
        Assert.Contains(audit.Events, entry => entry.EventCode == "SETTLEMENT_CREATED");
    }

    private (long PartnerId, long ProductId, long RequestId) SeedApprovedAssignedMurabahaPrerequisites()
    {
        using var scope = _factory.Services.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<CashLineErpDbContext>();

        var partnerService = new IslamicFinancePartnerService(new IslamicFinancePartnerRepository(dbContext));
        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var facilityService = new IslamicFinanceFacilityService(new IslamicFinanceFacilityRepository(dbContext));
        var requestRepository = new IslamicFinanceRequestRepository(dbContext);
        var requestService = new IslamicFinanceRequestService(requestRepository);
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));

        var partner = CreateActivePartner(partnerService);
        var product = CreateActiveProduct(productService);
        var facility = CreateActiveFacility(facilityService, partner.Id, product.Id);

        var request = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = $"REQ-MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FacilityId = facility.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 9101,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 120000m
        });
        requestService.SubmitRequest(request.Id, 3);
        requestService.ApproveRequest(request.Id, 4);

        var review = shariahReviewService.CreateReviewCase(new CreateIslamicFinanceShariahReviewRequest
        {
            ReviewCaseNumber = $"SHR-MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            ProductProfileId = product.Id,
            FinancingRequestId = request.Id,
            ReviewReasonCode = "SHARIAH_REVIEW"
        });
        shariahReviewService.SubmitReviewCase(review.Id, 3);
        shariahReviewService.RecommendOutcome(review.Id, new RecommendIslamicFinanceShariahOutcomeRequest
        {
            ReviewOutcomeCode = "APPROVED",
            ReviewNotes = "Approved for HTTP Murabaha case creation."
        }, 3);
        shariahReviewService.ApproveOutcome(review.Id, 4);

        requestService.AttachShariahReview(request.Id, new AttachIslamicFinanceShariahReviewRequest
        {
            ShariahReviewCaseId = review.Id
        }, 3);
        requestRepository.AssignPartnerLender(request.Id, partner.Id, 3);

        return (partner.Id, product.Id, request.Id);
    }

    private (long PartnerId, long ProductId, long RequestId) SeedUnassignedMurabahaPrerequisites()
    {
        using var scope = _factory.Services.CreateScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<CashLineErpDbContext>();

        var partnerService = new IslamicFinancePartnerService(new IslamicFinancePartnerRepository(dbContext));
        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var requestService = new IslamicFinanceRequestService(new IslamicFinanceRequestRepository(dbContext));

        var partner = CreateActivePartner(partnerService);
        var product = CreateActiveProduct(productService);

        var request = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = $"REQ-MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 9102,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 120000m
        });
        requestService.SubmitRequest(request.Id, 3);
        requestService.ApproveRequest(request.Id, 4);

        return (partner.Id, product.Id, request.Id);
    }

    private static IslamicFinancePartnerLenderResponse CreateActivePartner(IslamicFinancePartnerService service)
    {
        var existing = service
            .GetPartnerLenders(activeOnly: true)
            .FirstOrDefault(entry => entry.PartnerLenderPartyId == 1);
        if (existing is not null)
        {
            return existing;
        }

        var created = service.CreatePartnerLender(new CreateIslamicFinancePartnerLenderRequest
        {
            PartnerLenderPartyId = 1,
            DefaultCurrencyCode = "EGP",
            SupportsMurabaha = true,
            SupportsReceivablesFinance = false,
            SupportsTawarruq = false
        });

        service.SubmitPartnerLender(created.Id, 3);
        service.ApprovePartnerLender(created.Id, 4);
        return service.ActivatePartnerLender(created.Id, 4);
    }

    private static IslamicFinanceProductProfileResponse CreateActiveProduct(IslamicFinanceProductService service)
    {
        var created = service.CreateProductProfile(new CreateIslamicFinanceProductProfileRequest
        {
            ProductType = "MURABAHA",
            ProductCode = $"MUR-HTTP-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            ProductName = "Murabaha HTTP Product",
            RequiresShariahReview = true,
            AllowsPartnerFunding = true,
            AllowsCashLineFunding = false
        });

        service.SubmitProductProfile(created.Id, 3);
        service.ApproveProductProfile(created.Id, 4);
        return service.ActivateProductProfile(created.Id, 4);
    }

    private static IslamicFinanceFacilityResponse CreateActiveFacility(
        IslamicFinanceFacilityService service,
        long partnerLenderId,
        long productProfileId)
    {
        var created = service.CreateFacility(new CreateIslamicFinanceFacilityRequest
        {
            PartnerLenderId = partnerLenderId,
            CustomerPartyId = 1,
            ProductProfileId = productProfileId,
            FacilityNumber = $"FAC-HTTP-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            CurrencyCode = "EGP",
            ApprovedLimitAmount = 300000m,
            UtilizedAmount = 0m
        });

        service.SubmitFacility(created.Id, 3);
        service.ApproveFacility(created.Id, 4);
        return service.ActivateFacility(created.Id, 4);
    }
}
