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

namespace CashLineERP.Domain.IslamicFinance.Facilities;

public sealed class IslamicFinanceFacility : Entity
{
    public long Id { get; private set; }
    public long PartnerLenderId { get; private set; }
    public long CustomerPartyId { get; private set; }
    public long ProductProfileId { get; private set; }
    public string FacilityNumber { get; private set; } = string.Empty;
    public string CurrencyCode { get; private set; } = string.Empty;
    public decimal ApprovedLimitAmount { get; private set; }
    public decimal UtilizedAmount { get; private set; }
    public string StatusCode { get; private set; } = IslamicFinanceWorkflowCatalog.Draft;
    public bool IsActive { get; private set; }
    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? ActivatedAtUtc { get; private set; }
    public long? ActivatedByUserId { get; private set; }
    [NotMapped]
    public IslamicFinanceLifecycleState LifecycleState => IslamicFinanceWorkflowCatalog.ParseStatusCode(StatusCode);
    [NotMapped]
    public IslamicFinanceFacilityCapacity Capacity => new(ApprovedLimitAmount, UtilizedAmount);

    public IslamicFinanceFacility(
        long partnerLenderId,
        long customerPartyId,
        long productProfileId,
        string facilityNumber,
        string currencyCode,
        decimal approvedLimitAmount,
        decimal utilizedAmount = 0m)
    {
        if (partnerLenderId <= 0)
        {
            throw new InvalidOperationException("Islamic Finance facility requires a valid partner lender identifier.");
        }

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

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

        if (string.IsNullOrWhiteSpace(facilityNumber))
        {
            throw new InvalidOperationException("Islamic Finance facility number is required.");
        }

        PartnerLenderId = partnerLenderId;
        CustomerPartyId = customerPartyId;
        ProductProfileId = productProfileId;
        FacilityNumber = facilityNumber.Trim().ToUpperInvariant();
        CurrencyCode = NormalizeCurrencyCode(currencyCode);
        ApprovedLimitAmount = approvedLimitAmount;
        UtilizedAmount = utilizedAmount;

        _ = Capacity;
    }

    private IslamicFinanceFacility()
    {
    }

    public decimal AvailableAmount() => Capacity.AvailableAmount;

    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 facilities can be submitted.");
        }

        StatusCode = IslamicFinanceWorkflowCatalog.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        ActivatedAtUtc = null;
        ActivatedByUserId = 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 facilities can be approved.");
        }

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

        StatusCode = IslamicFinanceWorkflowCatalog.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Activate(long actorUserId)
    {
        if (!string.Equals(StatusCode, IslamicFinanceWorkflowCatalog.Approved, StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Only approved Islamic Finance facilities can be activated.");
        }

        IsActive = true;
        StatusCode = IslamicFinanceWorkflowCatalog.Active;
        ActivatedAtUtc = DateTime.UtcNow;
        ActivatedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Allocate(decimal amount, long actorUserId)
    {
        if (amount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance facility allocation amount must be greater than zero.");
        }

        var updatedUtilizedAmount = UtilizedAmount + amount;
        _ = new IslamicFinanceFacilityCapacity(ApprovedLimitAmount, updatedUtilizedAmount);
        UtilizedAmount = updatedUtilizedAmount;
        Touch(actorUserId);
    }

    public void Release(decimal amount, long actorUserId)
    {
        if (amount <= 0m)
        {
            throw new InvalidOperationException("Islamic Finance facility release amount must be greater than zero.");
        }

        var updatedUtilizedAmount = UtilizedAmount - amount;
        if (updatedUtilizedAmount < 0m)
        {
            throw new InvalidOperationException("Islamic Finance facility release amount cannot reduce utilization below zero.");
        }

        _ = new IslamicFinanceFacilityCapacity(ApprovedLimitAmount, updatedUtilizedAmount);
        UtilizedAmount = updatedUtilizedAmount;
        Touch(actorUserId);
    }

    private static string NormalizeCurrencyCode(string currencyCode)
    {
        var normalized = currencyCode?.Trim().ToUpperInvariant();
        if (string.IsNullOrWhiteSpace(normalized))
        {
            throw new InvalidOperationException("Islamic Finance facility currency code is required.");
        }

        return normalized;
    }
}
