using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.IslamicFinance;
using System.ComponentModel.DataAnnotations.Schema;

namespace CashLineERP.Domain.IslamicFinance.Requests;

public sealed class IslamicFinanceRequest : Entity
{
    public long Id { get; private set; }
    public string RequestNumber { get; private set; } = string.Empty;
    public long CustomerPartyId { get; private set; }
    public long ApplicantPartyId { get; private set; }
    public string ApplicantPartyTypeCode { get; private set; } = string.Empty;
    public long ProductProfileId { get; private set; }
    public long? FacilityId { get; private set; }
    public string FundingSourceMode { get; private set; } = IslamicFinanceProductCatalog.ToCode(IslamicFinanceFundingSourceMode.Partner);
    public string SourceDocumentType { get; private set; } = string.Empty;
    public long SourceDocumentId { get; private set; }
    public string TransactionCurrencyCode { get; private set; } = string.Empty;
    public decimal RequestedAmount { get; private set; }
    public string StatusCode { get; private set; } = IslamicFinanceWorkflowCatalog.Draft;
    public long? WorkflowInstanceId { get; private set; }
    public long? ShariahReviewCaseId { get; private set; }
    public long? AssignedPartnerLenderId { get; private set; }
    public long? SelectedFinancingDecisionId { get; private set; }
    public DateTime? SubmittedAtUtc { get; private set; }
    public long? SubmittedByUserId { get; private set; }
    public DateTime? ApprovedAtUtc { get; private set; }
    public long? ApprovedByUserId { get; private set; }
    public DateTime? ReturnedAtUtc { get; private set; }
    public long? ReturnedByUserId { get; private set; }
    public DateTime? RejectedAtUtc { get; private set; }
    public long? RejectedByUserId { get; private set; }
    public DateTime? AssignedPartnerLenderAtUtc { get; private set; }
    public long? AssignedPartnerLenderByUserId { get; private set; }
    public DateTime? SelectedOfferAtUtc { get; private set; }
    public long? SelectedOfferByUserId { get; private set; }
    public DateTime? ShariahReviewAttachedAtUtc { get; private set; }
    public long? ShariahReviewAttachedByUserId { get; private set; }
    [NotMapped]
    public IslamicFinanceFundingSourceMode FundingSourceModeValue => IslamicFinanceProductCatalog.ParseFundingSourceMode(FundingSourceMode);
    [NotMapped]
    public IslamicFinanceLifecycleState LifecycleState => IslamicFinanceWorkflowCatalog.ParseStatusCode(StatusCode);
    [NotMapped]
    public IslamicFinanceRequestSourceReference SourceReference => new(SourceDocumentType, SourceDocumentId);
    [NotMapped]
    public IslamicFinanceRequestAmount Amount => new(TransactionCurrencyCode, RequestedAmount);

    public IslamicFinanceRequest(
        string requestNumber,
        long customerPartyId,
        long applicantPartyId,
        string applicantPartyTypeCode,
        long productProfileId,
        long? facilityId,
        IslamicFinanceFundingSourceMode fundingSourceMode,
        string sourceDocumentType,
        long sourceDocumentId,
        string transactionCurrencyCode,
        decimal requestedAmount)
    {
        if (string.IsNullOrWhiteSpace(requestNumber))
        {
            throw new InvalidOperationException("Islamic Finance request number is required.");
        }

        if (customerPartyId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request requires a valid customer party identifier.");
        }

        if (applicantPartyId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request requires a valid applicant party identifier.");
        }

        if (productProfileId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request requires a valid product profile identifier.");
        }

        if (facilityId.HasValue && facilityId.Value <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request facility identifier must be a valid positive identifier when provided.");
        }

        RequestNumber = requestNumber.Trim().ToUpperInvariant();
        CustomerPartyId = customerPartyId;
        ApplicantPartyId = applicantPartyId;
        ApplicantPartyTypeCode = NormalizeApplicantPartyTypeCode(applicantPartyTypeCode);
        ProductProfileId = productProfileId;
        FacilityId = facilityId;
        FundingSourceMode = IslamicFinanceProductCatalog.ToCode(fundingSourceMode);
        SourceDocumentType = new IslamicFinanceRequestSourceReference(sourceDocumentType, sourceDocumentId).SourceDocumentType;
        SourceDocumentId = sourceDocumentId;
        TransactionCurrencyCode = new IslamicFinanceRequestAmount(transactionCurrencyCode, requestedAmount).TransactionCurrencyCode;
        RequestedAmount = requestedAmount;
    }

    private IslamicFinanceRequest()
    {
    }

    public void Submit(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Draft, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Returned, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only draft or returned Islamic Finance requests can be submitted.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        ReturnedAtUtc = null;
        ReturnedByUserId = null;
        RejectedAtUtc = null;
        RejectedByUserId = null;
        Touch(actorUserId);
    }

    public void Approve(long actorUserId)
    {
        if (string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only submitted Islamic Finance requests can be approved.");
        }

        if (SubmittedByUserId == actorUserId)
        {
            throw new InvalidOperationException("Islamic Finance request maker-checker control prohibits self-approval.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        ReturnedAtUtc = null;
        ReturnedByUserId = null;
        RejectedAtUtc = null;
        RejectedByUserId = null;
        Touch(actorUserId);
    }

    public void Return(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only submitted Islamic Finance requests can be returned.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Returned;
        ReturnedAtUtc = DateTime.UtcNow;
        ReturnedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Reject(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Submitted, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only submitted Islamic Finance requests can be rejected.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Rejected;
        RejectedAtUtc = DateTime.UtcNow;
        RejectedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void AssignPartnerLender(long partnerLenderId, long actorUserId)
    {
        if (partnerLenderId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request partner-lender assignment requires a valid positive identifier.");
        }

        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Assigned, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance request can assign a partner lender only after approval.");
        }

        AssignedPartnerLenderId = partnerLenderId;
        StatusCode = IslamicFinanceWorkflowCatalog.Assigned;
        AssignedPartnerLenderAtUtc = DateTime.UtcNow;
        AssignedPartnerLenderByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void SelectOffer(long financingDecisionId, long partnerLenderId, long actorUserId)
    {
        if (financingDecisionId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance financing request selected offer requires a valid financing decision identifier.");
        }

        AssignPartnerLender(partnerLenderId, actorUserId);
        SelectedFinancingDecisionId = financingDecisionId;
        SelectedOfferAtUtc = DateTime.UtcNow;
        SelectedOfferByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void AttachShariahReview(long shariahReviewCaseId, long actorUserId)
    {
        if (shariahReviewCaseId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance request Shariah review reference must be a valid positive identifier.");
        }

        ShariahReviewCaseId = shariahReviewCaseId;
        ShariahReviewAttachedAtUtc = DateTime.UtcNow;
        ShariahReviewAttachedByUserId = actorUserId;
        Touch(actorUserId);
    }

    private static string NormalizeApplicantPartyTypeCode(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
        {
            throw new InvalidOperationException("Islamic Finance request applicant party type is required.");
        }

        var normalizedValue = value.Trim().ToUpperInvariant();
        if (!string.Equals(normalizedValue, "CUSTOMER", StringComparison.OrdinalIgnoreCase)
            && !string.Equals(normalizedValue, "SUPPLIER", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Islamic Finance request applicant party type must be CUSTOMER or SUPPLIER.");
        }

        return normalizedValue;
    }
}
