using System.Linq;
using CashLineERP.Application.IslamicFinance.ContractTemplates;
using CashLineERP.Application.IslamicFinance.Facilities;
using CashLineERP.Application.IslamicFinance.Disbursements;
using CashLineERP.Application.IslamicFinance.Murabaha;
using CashLineERP.Application.IslamicFinance.PartnerLenders;
using CashLineERP.Application.IslamicFinance.ProductProfiles;
using CashLineERP.Application.IslamicFinance.Requests;
using CashLineERP.Application.IslamicFinance.Settlements;
using CashLineERP.Application.IslamicFinance.Shariah;
using CashLineERP.Contracts.IslamicFinance;
using CashLineERP.Domain.IslamicFinance.Murabaha;
using CashLineERP.Infrastructure.Persistence.Repositories;
using CashLineERP.Infrastructure.Persistence.Repositories.IslamicFinance;

namespace CashLineERP.Api.Tests;

public sealed class IslamicFinanceMurabahaCaseServiceQaTests
{
    [Fact]
    public void Murabaha_Case_Can_Be_Created_Submitted_Approved_Activated_And_Read_Back()
    {
        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 requestRepository = new IslamicFinanceRequestRepository(dbContext);
        var requestService = new IslamicFinanceRequestService(requestRepository);
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));
        var murabahaService = new MurabahaCaseService(new MurabahaCaseRepository(dbContext));

        var partner = CreateActivePartner(partnerService);
        var product = CreateActiveProduct(productService);
        var facility = CreateActiveFacility(facilityService, partner.Id, product.Id);
        var request = CreateApprovedAssignedRequest(requestService, requestRepository, shariahReviewService, partner.Id, product.Id, facility.Id);

        var created = murabahaService.CreateCase(new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = "MUR-SVC-001",
            FinancingRequestId = request.Id,
            CustomerPartyId = 1,
            PartnerLenderId = partner.Id,
            SupplierPartyId = 2,
            ProductProfileId = product.Id,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        });

        Assert.Equal("DRAFT", created.StatusCode);
        Assert.Equal(120000m, created.SalePriceAmount);

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

        var approved = murabahaService.ApproveCase(created.Id, 4);
        Assert.Equal("APPROVED", approved.StatusCode);

        var activated = murabahaService.ActivateCase(created.Id, 4);
        Assert.Equal("ACTIVE", activated.StatusCode);

        var detail = murabahaService.GetCase(created.Id);
        Assert.NotNull(detail);
        Assert.Equal(created.Id, detail!.Id);

        var list = murabahaService.GetCases(statusCode: "ACTIVE", financingRequestId: request.Id, partnerLenderId: partner.Id);
        Assert.Single(list);
    }

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

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

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

        var request = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = "REQ-MUR-002",
            CustomerPartyId = 1,
            ProductProfileId = product.Id,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 9002,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 125000m
        });

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

        var error = Assert.Throws<InvalidOperationException>(() => murabahaService.CreateCase(new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = "MUR-SVC-002",
            FinancingRequestId = request.Id,
            CustomerPartyId = 1,
            PartnerLenderId = partner.Id,
            SupplierPartyId = 2,
            ProductProfileId = product.Id,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        }));

        Assert.Contains("assigned financing request", error.Message, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void Murabaha_Case_Service_Returns_History_And_Audit_Across_Linked_Runtime()
    {
        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 templateService = new IslamicFinanceContractTemplateService(new IslamicFinanceContractTemplateRepository(dbContext));
        var requestRepository = new IslamicFinanceRequestRepository(dbContext);
        var requestService = new IslamicFinanceRequestService(requestRepository);
        var shariahReviewService = new ShariahReviewService(new ShariahReviewRepository(dbContext));
        var murabahaCaseService = new MurabahaCaseService(new MurabahaCaseRepository(dbContext));
        var murabahaContractService = new IslamicFinanceMurabahaContractService(
            new IslamicFinanceMurabahaContractRepository(dbContext, new DocumentStorageRepository(dbContext)));
        var disbursementService = new IslamicFinanceMurabahaDisbursementService(new IslamicFinanceMurabahaDisbursementRepository(dbContext));
        var settlementService = new IslamicFinanceSettlementService(new IslamicFinanceSettlementRepository(dbContext));

        var partner = CreateActivePartner(partnerService);
        var product = CreateActiveProduct(productService);
        var facility = CreateActiveFacility(facilityService, partner.Id, product.Id);
        var template = CreateActiveTemplate(templateService, product.Id);
        var request = CreateApprovedAssignedRequest(requestService, requestRepository, shariahReviewService, partner.Id, product.Id, facility.Id);

        var murabahaCase = murabahaCaseService.CreateCase(new CreateIslamicFinanceMurabahaCaseRequest
        {
            CaseNumber = "MUR-HIST-001",
            FinancingRequestId = request.Id,
            CustomerPartyId = 1,
            PartnerLenderId = partner.Id,
            SupplierPartyId = 2,
            ProductProfileId = product.Id,
            ContractTemplateId = template.Id,
            TransactionCurrencyCode = "EGP",
            PurchaseCostAmount = 100000m,
            ProfitAmount = 20000m
        });

        murabahaCaseService.SubmitCase(murabahaCase.Id, 3);
        murabahaCaseService.ApproveCase(murabahaCase.Id, 4);
        murabahaCaseService.ActivateCase(murabahaCase.Id, 4);
        murabahaContractService.GenerateContract(murabahaCase.Id, 4, new GenerateIslamicFinanceMurabahaContractRequest());
        dbContext.IslamicFinanceProfitSchedules.AddRange(
            new IslamicFinanceProfitSchedule(murabahaCase.Id, 1, new DateOnly(2026, 5, 1), 50000m, 10000m, 60000m),
            new IslamicFinanceProfitSchedule(murabahaCase.Id, 2, new DateOnly(2026, 6, 1), 50000m, 10000m, 0m));
        dbContext.SaveChanges();

        var disbursement = disbursementService.CreateDisbursement(new CreateIslamicFinanceMurabahaDisbursementRequest
        {
            MurabahaCaseId = murabahaCase.Id,
            DisbursementNumber = "IFD-HIST-0001",
            TargetType = "SUPPLIER_PAYMENT",
            InstructedAmount = 60000m,
            PlannedExecutionDate = new DateOnly(2026, 4, 7)
        });
        disbursementService.SubmitDisbursement(disbursement.Id, 10);
        disbursementService.ApproveDisbursement(disbursement.Id, 11);
        var executed = disbursementService.ExecuteDisbursement(disbursement.Id, new ExecuteIslamicFinanceMurabahaDisbursementRequest
        {
            ExecutionReferenceNumber = "PAY-HIST-0001"
        }, 12);

        settlementService.CreateSettlement(new CreateIslamicFinanceSettlementRequest
        {
            SettlementNumber = "IFS-HIST-0001",
            MurabahaCaseId = murabahaCase.Id,
            SettlementDate = new DateOnly(2026, 4, 10),
            SettlementCurrencyCode = "EGP",
            Lines =
            [
                new CreateIslamicFinanceSettlementLineRequest
                {
                    MurabahaDisbursementId = executed.Id,
                    SettledAmount = executed.ExecutedAmount
                }
            ]
        });

        var history = murabahaCaseService.GetCaseHistory(murabahaCase.Id);
        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 audit = murabahaCaseService.GetCaseAudit(murabahaCase.Id);
        Assert.NotNull(audit);
        Assert.Equal(murabahaCase.Id, audit!.MurabahaCase.Id);
        Assert.Contains(audit.Events, entry => entry.EventCode == "SETTLEMENT_CREATED");
    }

    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 = service.CreateProductProfile(new CreateIslamicFinanceProductProfileRequest
        {
            ProductType = "MURABAHA",
            ProductCode = $"MUR-CASE-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            ProductName = "Murabaha Case 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-MUR-{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);
    }

    private static IslamicFinanceContractTemplateResponse CreateActiveTemplate(
        IslamicFinanceContractTemplateService service,
        long productProfileId)
    {
        var created = service.CreateContractTemplate(new CreateIslamicFinanceContractTemplateRequest
        {
            ProductProfileId = productProfileId,
            TemplateCode = $"TPL-HIST-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            TemplateName = "Murabaha History Template",
            LanguageCode = "EN"
        });

        service.SubmitContractTemplate(created.Id, 3);
        service.ApproveContractTemplate(created.Id, 4);
        return service.ActivateContractTemplate(created.Id, 4);
    }

    private static IslamicFinanceFinancingRequestResponse CreateApprovedAssignedRequest(
        IslamicFinanceRequestService requestService,
        IslamicFinanceRequestRepository requestRepository,
        ShariahReviewService shariahReviewService,
        long partnerLenderId,
        long productProfileId,
        long facilityId)
    {
        var created = requestService.CreateRequest(new CreateIslamicFinanceFinancingRequestRequest
        {
            RequestNumber = $"REQ-MUR-{Guid.NewGuid():N}"[..12].ToUpperInvariant(),
            CustomerPartyId = 1,
            ProductProfileId = productProfileId,
            FacilityId = facilityId,
            FundingSourceMode = "PARTNER",
            SourceDocumentType = "SALES_ORDER",
            SourceDocumentId = 9001,
            TransactionCurrencyCode = "EGP",
            RequestedAmount = 120000m
        });

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

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

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

        var assigned = requestRepository.AssignPartnerLender(created.Id, partnerLenderId, 3);
        return new IslamicFinanceFinancingRequestResponse
        {
            Id = assigned.FinancingRequestId,
            RequestNumber = assigned.RequestNumber,
            CustomerPartyId = assigned.CustomerPartyId,
            ProductProfileId = assigned.ProductProfileId,
            FacilityId = assigned.FacilityId,
            FundingSourceMode = assigned.FundingSourceMode,
            SourceDocumentType = assigned.SourceDocumentType,
            SourceDocumentId = assigned.SourceDocumentId,
            TransactionCurrencyCode = assigned.TransactionCurrencyCode,
            RequestedAmount = assigned.RequestedAmount,
            StatusCode = assigned.StatusCode,
            WorkflowInstanceId = assigned.WorkflowInstanceId,
            ShariahReviewCaseId = assigned.ShariahReviewCaseId,
            AssignedPartnerLenderId = assigned.AssignedPartnerLenderId,
            SubmittedAtUtc = assigned.SubmittedAtUtc,
            SubmittedByUserId = assigned.SubmittedByUserId,
            ApprovedAtUtc = assigned.ApprovedAtUtc,
            ApprovedByUserId = assigned.ApprovedByUserId
        };
    }
}
