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

namespace CashLineERP.Domain.IslamicFinance.Disbursements;

public sealed class IslamicFinanceMurabahaDisbursement : Entity
{
    public long Id { get; private set; }
    public string DisbursementNumber { get; private set; } = string.Empty;
    public long MurabahaCaseId { get; private set; }
    public long CustomerPartyId { get; private set; }
    public long PartnerLenderId { get; private set; }
    public long? SupplierPartyId { get; private set; }
    public string FundingSourceMode { get; private set; } = IslamicFinanceProductCatalog.ToCode(IslamicFinanceFundingSourceMode.Partner);
    public string TargetType { get; private set; } = IslamicFinanceDisbursementCatalog.SupplierPayment;
    public string TransactionCurrencyCode { get; private set; } = string.Empty;
    public decimal InstructedAmount { get; private set; }
    public decimal ExecutedAmount { get; private set; }
    public DateOnly PlannedExecutionDate { get; private set; }
    public string StatusCode { get; private set; } = IslamicFinanceWorkflowCatalog.Draft;
    public long? WorkflowInstanceId { 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? ExecutedAtUtc { get; private set; }
    public long? ExecutedByUserId { get; private set; }
    public string? ExecutionReferenceNumber { get; private set; }
    public string? ExecutionNotes { get; private set; }
    public DateTime? SuspendedAtUtc { get; private set; }
    public long? SuspendedByUserId { get; private set; }
    public string? SuspensionReasonCode { get; private set; }

    [NotMapped]
    public IslamicFinanceFundingSourceMode FundingSourceModeValue => IslamicFinanceProductCatalog.ParseFundingSourceMode(FundingSourceMode);

    [NotMapped]
    public IslamicFinanceDisbursementTargetType TargetTypeValue => IslamicFinanceDisbursementCatalog.ParseTargetType(TargetType);

    [NotMapped]
    public IslamicFinanceLifecycleState LifecycleState => IslamicFinanceWorkflowCatalog.ParseStatusCode(StatusCode);

    [NotMapped]
    public IslamicFinanceMurabahaDisbursementAmount Amount => new(InstructedAmount, ExecutedAmount);

    [NotMapped]
    public IslamicFinanceMurabahaDisbursementExecution Execution => new(
        ExecutionReferenceNumber,
        ExecutedAtUtc,
        ExecutedByUserId,
        ExecutionNotes);

    public IslamicFinanceMurabahaDisbursement(
        string disbursementNumber,
        long murabahaCaseId,
        long customerPartyId,
        long partnerLenderId,
        long? supplierPartyId,
        IslamicFinanceFundingSourceMode fundingSourceMode,
        IslamicFinanceDisbursementTargetType targetType,
        string transactionCurrencyCode,
        decimal instructedAmount,
        DateOnly plannedExecutionDate)
    {
        if (string.IsNullOrWhiteSpace(disbursementNumber))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement number is required.");
        }

        if (murabahaCaseId <= 0 || customerPartyId <= 0 || partnerLenderId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement requires valid case, customer, and partner-lender identifiers.");
        }

        if (supplierPartyId.HasValue && supplierPartyId.Value <= 0)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha supplier party identifier must be positive when provided.");
        }

        if (string.IsNullOrWhiteSpace(transactionCurrencyCode))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement transaction currency code is required.");
        }

        if (instructedAmount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement instructed amount must be positive.");
        }

        if (fundingSourceMode != IslamicFinanceFundingSourceMode.Partner)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement currently supports partner-funded execution only.");
        }

        if (targetType == IslamicFinanceDisbursementTargetType.SupplierPayment && !supplierPartyId.HasValue)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha supplier-payment disbursement requires a supplier party.");
        }

        DisbursementNumber = disbursementNumber.Trim().ToUpperInvariant();
        MurabahaCaseId = murabahaCaseId;
        CustomerPartyId = customerPartyId;
        PartnerLenderId = partnerLenderId;
        SupplierPartyId = supplierPartyId;
        FundingSourceMode = IslamicFinanceProductCatalog.ToCode(fundingSourceMode);
        TargetType = IslamicFinanceDisbursementCatalog.ToCode(targetType);
        TransactionCurrencyCode = transactionCurrencyCode.Trim().ToUpperInvariant();
        InstructedAmount = instructedAmount;
        PlannedExecutionDate = plannedExecutionDate;
    }

    private IslamicFinanceMurabahaDisbursement()
    {
    }

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

        if (workflowInstanceId.HasValue && workflowInstanceId.Value <= 0)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement workflow instance identifier must be positive when provided.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Submitted;
        WorkflowInstanceId = workflowInstanceId ?? WorkflowInstanceId;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = 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 Murabaha disbursements can be approved.");
        }

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

        StatusCode = IslamicFinanceWorkflowCatalog.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        SuspensionReasonCode = null;
        SuspendedAtUtc = null;
        SuspendedByUserId = null;
        Touch(actorUserId);
    }

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

        StatusCode = IslamicFinanceWorkflowCatalog.Returned;
        Touch(actorUserId);
    }

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

        StatusCode = IslamicFinanceWorkflowCatalog.Rejected;
        Touch(actorUserId);
    }

    public void Execute(long actorUserId, string executionReferenceNumber, DateTime executedAtUtc, decimal? executedAmount = null, string? executionNotes = null)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only approved Islamic Finance Murabaha disbursements can be executed.");
        }

        if (string.IsNullOrWhiteSpace(executionReferenceNumber))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement execution reference number is required.");
        }

        var normalizedExecutedAmount = executedAmount ?? InstructedAmount;
        if (normalizedExecutedAmount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement executed amount must be positive.");
        }

        if (normalizedExecutedAmount > InstructedAmount)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement executed amount cannot exceed instructed amount.");
        }

        ExecutedAmount = normalizedExecutedAmount;
        ExecutedAtUtc = executedAtUtc.Kind == DateTimeKind.Utc ? executedAtUtc : executedAtUtc.ToUniversalTime();
        ExecutedByUserId = actorUserId;
        ExecutionReferenceNumber = executionReferenceNumber.Trim().ToUpperInvariant();
        ExecutionNotes = string.IsNullOrWhiteSpace(executionNotes) ? null : executionNotes.Trim();
        StatusCode = IslamicFinanceWorkflowCatalog.Active;
        SuspensionReasonCode = null;
        SuspendedAtUtc = null;
        SuspendedByUserId = null;
        Touch(actorUserId);
    }

    public void Suspend(long actorUserId, string reasonCode)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase)
            && !string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only approved or active Islamic Finance Murabaha disbursements can be suspended.");
        }

        if (string.IsNullOrWhiteSpace(reasonCode))
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement suspension reason code is required.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Suspended;
        SuspendedAtUtc = DateTime.UtcNow;
        SuspendedByUserId = actorUserId;
        SuspensionReasonCode = reasonCode.Trim().ToUpperInvariant();
        Touch(actorUserId);
    }

    public void Close(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Active, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only active Islamic Finance Murabaha disbursements can be closed.");
        }

        if (ExecutedAmount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance Murabaha disbursement cannot close before execution is recorded.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Closed;
        Touch(actorUserId);
    }
}
