using CashLineERP.Domain.Common;
using CashLineERP.SharedKernel.Workflow;

namespace CashLineERP.Domain.Sales;

public sealed class PricingRule : Entity
{
    public long Id { get; private set; }

    public string RuleCode { get; private set; } = string.Empty;

    public string RuleName { get; private set; } = string.Empty;

    public long? BranchId { get; private set; }

    public long? CustomerId { get; private set; }

    public long ItemId { get; private set; }

    public string CurrencyCode { get; private set; } = "EGP";

    public string UnitOfMeasureCode { get; private set; } = string.Empty;

    public decimal? MinimumQuantity { get; private set; }

    public decimal? MaximumQuantity { get; private set; }

    public decimal FixedUnitPrice { get; private set; }

    public int Priority { get; private set; }

    public DateOnly EffectiveFromDate { get; private set; }

    public DateOnly? EffectiveToDate { get; private set; }

    public long StatusId { get; private set; } = WorkflowStatusSeedIds.Draft;

    public bool IsActive { get; private set; }

    public string? Notes { 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; }

    public PricingRule(
        string ruleCode,
        string ruleName,
        long? branchId,
        long? customerId,
        long itemId,
        string currencyCode,
        string unitOfMeasureCode,
        decimal? minimumQuantity,
        decimal? maximumQuantity,
        decimal fixedUnitPrice,
        int priority,
        DateOnly effectiveFromDate,
        DateOnly? effectiveToDate,
        string? notes)
    {
        RuleCode = ruleCode;
        RuleName = ruleName;
        BranchId = branchId;
        CustomerId = customerId;
        ItemId = itemId;
        CurrencyCode = currencyCode;
        UnitOfMeasureCode = unitOfMeasureCode;
        MinimumQuantity = minimumQuantity;
        MaximumQuantity = maximumQuantity;
        FixedUnitPrice = fixedUnitPrice;
        Priority = priority;
        EffectiveFromDate = effectiveFromDate;
        EffectiveToDate = effectiveToDate;
        Notes = notes;
    }

    private PricingRule()
    {
    }

    public void Submit(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Draft && StatusId != WorkflowStatusSeedIds.Returned)
        {
            throw new InvalidOperationException("Only draft or returned pricing rules can be submitted.");
        }

        StatusId = WorkflowStatusSeedIds.Submitted;
        SubmittedAtUtc = DateTime.UtcNow;
        SubmittedByUserId = actorUserId;
        ApprovedAtUtc = null;
        ApprovedByUserId = null;
        Touch(actorUserId);
    }

    public void Approve(long actorUserId)
    {
        if (StatusId == WorkflowStatusSeedIds.Approved)
        {
            return;
        }

        if (StatusId != WorkflowStatusSeedIds.Submitted)
        {
            throw new InvalidOperationException("Only submitted pricing rules can be approved.");
        }

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

        StatusId = WorkflowStatusSeedIds.Approved;
        ApprovedAtUtc = DateTime.UtcNow;
        ApprovedByUserId = actorUserId;
        Touch(actorUserId);
    }

    public void Activate(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Approved)
        {
            throw new InvalidOperationException("Only approved pricing rules can be activated.");
        }

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

    public bool AppliesTo(
        long branchId,
        long customerId,
        long itemId,
        string currencyCode,
        string unitOfMeasureCode,
        decimal orderedQuantity,
        DateOnly pricingDate) =>
        IsActive
        && StatusId == WorkflowStatusSeedIds.Approved
        && ItemId == itemId
        && string.Equals(CurrencyCode, currencyCode, StringComparison.OrdinalIgnoreCase)
        && string.Equals(UnitOfMeasureCode, unitOfMeasureCode, StringComparison.OrdinalIgnoreCase)
        && (!BranchId.HasValue || BranchId.Value == branchId)
        && (!CustomerId.HasValue || CustomerId.Value == customerId)
        && (!MinimumQuantity.HasValue || orderedQuantity >= MinimumQuantity.Value)
        && (!MaximumQuantity.HasValue || orderedQuantity <= MaximumQuantity.Value)
        && pricingDate >= EffectiveFromDate
        && (!EffectiveToDate.HasValue || pricingDate <= EffectiveToDate.Value);

    public int GetSpecificityScore()
    {
        var score = 0;
        if (BranchId.HasValue)
        {
            score += 1;
        }

        if (CustomerId.HasValue)
        {
            score += 2;
        }

        if (MinimumQuantity.HasValue || MaximumQuantity.HasValue)
        {
            score += 1;
        }

        return score;
    }
}
