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.Repositories.IslamicFinance;

namespace CashLineERP.Api.Tests;

public sealed class IslamicFinanceRequestServiceQaTests
{
    [Fact]
    public void Financing_Request_Supports_Submit_Return_Resubmit_And_Approve()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

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

        var product = CreateDraftProduct(productService);
        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-001",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1001,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 175000m
        });

        Assert.Equal("DRAFT", created.StatusCode);

        var submitted = requestService.SubmitRequest(created.Id, 3);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var returned = requestService.ReturnRequest(created.Id, 4);
        Assert.Equal("RETURNED", returned.StatusCode);

        requestService.SubmitRequest(created.Id, 3);
        var approved = requestService.ApproveRequest(created.Id, 4);

        Assert.Equal("APPROVED", approved.StatusCode);

        var approvedList = requestService.GetRequests(statusCode: "APPROVED", customerPartyId: 1, productProfileId: product.Id);
        Assert.Single(approvedList);
    }

    [Fact]
    public void Financing_Request_Supports_Rejection_Path()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

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

        var product = CreateDraftProduct(productService);
        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-002",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1002,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 98000m
        });

        requestService.SubmitRequest(created.Id, 3);
        var rejected = requestService.RejectRequest(created.Id, 4);

        Assert.Equal("REJECTED", rejected.StatusCode);
    }

    [Fact]
    public void Financing_Request_Can_Attach_Shariah_Review_With_Matching_Product_And_Request()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var requestService = new IslamicFinanceRequestService(new IslamicFinanceRequestRepository(dbContext));
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));

        var product = CreateDraftProduct(productService);
        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-003",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1003,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 120000m
        });

        var review = shariahReviewService.CreateReviewCase(new CreateIslamicFinanceShariahReviewRequest
        {
            ReviewCaseNumber = "SHR-SVC-001",
            ProductProfileId = product.Id,
            FinancingRequestId = created.Id,
            ReviewReasonCode = "SHARIAH_REVIEW"
        });

        var attached = requestService.AttachShariahReview(created.Id, new AttachIslamicFinanceShariahReviewRequest
        {
            ShariahReviewCaseId = review.Id
        }, 3);

        Assert.Equal(review.Id, attached.ShariahReviewCaseId);
    }

    [Fact]
    public void Financing_Request_Attach_Shariah_Review_Blocks_Mismatched_Product()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var requestService = new IslamicFinanceRequestService(new IslamicFinanceRequestRepository(dbContext));
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));

        var requestProduct = CreateDraftProduct(productService);
        var reviewProduct = productService.CreateProductProfile(new CreateIslamicFinanceProductProfileRequest
        {
            ProductType = "MURABAHA",
            ProductCode = $"MUR-REQ-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            ProductName = "Other Murabaha Request Product",
            RequiresShariahReview = true,
            AllowsPartnerFunding = true,
            AllowsCashLineFunding = false
        });

        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-004",
            CustomerPartyId = 1,
            ProductProfileId = requestProduct.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1004,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 122000m
        });

        var review = shariahReviewService.CreateReviewCase(new CreateIslamicFinanceShariahReviewRequest
        {
            ReviewCaseNumber = "SHR-SVC-002",
            ProductProfileId = reviewProduct.Id,
            FinancingRequestId = created.Id,
            ReviewReasonCode = "SHARIAH_REVIEW"
        });

        var error = Assert.Throws<InvalidOperationException>(() => requestService.AttachShariahReview(created.Id, new AttachIslamicFinanceShariahReviewRequest
        {
            ShariahReviewCaseId = review.Id
        }, 3));

        Assert.Contains("same product profile", error.Message, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void Financing_Request_History_And_Decision_Audit_Reads_Preserve_Timeline()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

        var productService = new IslamicFinanceProductService(new IslamicFinanceProductRepository(dbContext));
        var requestRepository = new IslamicFinanceRequestRepository(dbContext);
        var requestService = new IslamicFinanceRequestService(requestRepository);
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));

        var product = CreateDraftProduct(productService);
        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-005",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1005,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 140000m
        });

        requestService.SubmitRequest(created.Id, 3);
        requestService.ApproveRequest(created.Id, 4);

        var review = shariahReviewService.CreateReviewCase(new CreateIslamicFinanceShariahReviewRequest
        {
            ReviewCaseNumber = "SHR-SVC-003",
            ProductProfileId = product.Id,
            FinancingRequestId = created.Id,
            ReviewReasonCode = "SHARIAH_REVIEW"
        });

        requestService.AttachShariahReview(created.Id, new AttachIslamicFinanceShariahReviewRequest
        {
            ShariahReviewCaseId = review.Id
        }, 3);

        var decision = requestRepository.CreateDecision(new CreateIslamicFinanceDecisionCommand(
            created.Id,
            "APPROVED",
            "Approved for partner processing.",
            1,
            null,
            "MURABAHA",
            "PARTNER",
            "EGP",
            140000m,
            12,
            8.5m,
            1500m,
            "Corporate guarantee",
            1));
        requestRepository.SubmitDecision(decision.FinancingDecisionId, 3);
        requestRepository.ApproveDecision(decision.FinancingDecisionId, 4);

        var history = requestService.GetRequestHistory(created.Id);
        Assert.Contains(history, item => item.EventCode == "REQUEST_CREATED");
        Assert.Contains(history, item => item.EventCode == "REQUEST_SUBMITTED");
        Assert.Contains(history, item => item.EventCode == "REQUEST_APPROVED");
        Assert.Contains(history, item => item.EventCode == "SHARIAH_REVIEW_ATTACHED");
        Assert.Contains(history, item => item.EventCode == "DECISION_CREATED");
        Assert.Contains(history, item => item.EventCode == "DECISION_APPROVED");

        var requestAudit = requestService.GetRequestAudit(created.Id);
        Assert.NotNull(requestAudit);
        Assert.Single(requestAudit!.Decisions);
        Assert.True(requestAudit.Events.Count >= 6);

        var decisionAudit = requestService.GetDecisionAudit(decision.FinancingDecisionId);
        Assert.NotNull(decisionAudit);
        Assert.Equal("APPROVED", decisionAudit!.Decision.StatusCode);
        Assert.Contains(decisionAudit.Events, item => item.EventCode == "DECISION_SUBMITTED");
        Assert.Contains(decisionAudit.Events, item => item.EventCode == "DECISION_APPROVED");
    }

    [Fact]
    public void Financing_Request_Supports_Partner_Assignment_And_Decision_Lifecycle()
    {
        using var dbContext = QaDbContextFactory.CreateContext();

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

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

        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-SVC-006",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FacilityId = facility.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 1006,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 150000m
        });

        requestService.SubmitRequest(created.Id, 3);
        requestService.ApproveRequest(created.Id, 4);

        var assigned = requestService.AssignPartnerLender(created.Id, new AssignIslamicFinancePartnerLenderRequest
        {
            PartnerLenderId = partner.Id
        }, 3);

        Assert.Equal("ASSIGNED", assigned.StatusCode);
        Assert.Equal(partner.Id, assigned.AssignedPartnerLenderId);

        var decision = requestService.CreateDecision(created.Id, new CreateIslamicFinanceDecisionRequest
        {
            DecisionCode = "APPROVED",
            DecisionNotes = "Approved by partner lender.",
            DecidingPartyId = 1,
            PartnerLenderId = partner.Id
        });

        Assert.Equal("DRAFT", decision.StatusCode);

        var submitted = requestService.SubmitDecision(decision.Id, 3);
        Assert.Equal("SUBMITTED", submitted.StatusCode);

        var approved = requestService.ApproveDecision(decision.Id, 4);
        Assert.Equal("APPROVED", approved.StatusCode);
        Assert.Equal(partner.Id, approved.PartnerLenderId);
    }

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

    private static IslamicFinancePartnerLenderResponse CreateActivePartner(IslamicFinancePartnerService service)
    {
        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 = CreateDraftProduct(service);
        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-SVC-{Guid.NewGuid():N}"[..15].ToUpperInvariant(),
            CurrencyCode = "EGP",
            ApprovedLimitAmount = 250000m,
            UtilizedAmount = 25000m
        });

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