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

namespace CashLineERP.Domain.Inventory;

public sealed class ReplenishmentRule : 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 TargetWarehouseId { get; private set; }
    public long? PreferredSourceWarehouseId { get; private set; }
    public long ItemId { get; private set; }
    public string UnitOfMeasureCode { get; private set; } = string.Empty;
    public decimal ReorderPointQuantity { get; private set; }

    public decimal TargetSafetyStockQuantity { get; private set; }

    public decimal TargetStockQuantity { get; private set; }
    public decimal SourceSafetyStockQuantity { 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 ReplenishmentRule(
        string ruleCode,
        string ruleName,
        long branchId,
        long targetWarehouseId,
        long? preferredSourceWarehouseId,
        long itemId,
        string unitOfMeasureCode,
        decimal reorderPointQuantity,
        decimal targetSafetyStockQuantity,
        decimal targetStockQuantity,
        decimal sourceSafetyStockQuantity,
        int priority,
        DateOnly effectiveFromDate,
        DateOnly? effectiveToDate,
        string? notes)
    {
        if (branchId <= 0)
        {
            throw new InvalidOperationException("Replenishment rule requires a valid branch.");
        }

        if (targetWarehouseId <= 0)
        {
            throw new InvalidOperationException("Replenishment rule requires a valid target warehouse.");
        }

        if (preferredSourceWarehouseId.HasValue && preferredSourceWarehouseId.Value == targetWarehouseId)
        {
            throw new InvalidOperationException("Preferred source warehouse must differ from the target warehouse.");
        }

        if (itemId <= 0)
        {
            throw new InvalidOperationException("Replenishment rule requires a valid item.");
        }

        if (reorderPointQuantity < 0m)
        {
            throw new InvalidOperationException("Reorder point quantity cannot be negative.");
        }

        if (targetStockQuantity <= 0m)
        {
            throw new InvalidOperationException("Target stock quantity must be greater than zero.");
        }

        if (targetSafetyStockQuantity < reorderPointQuantity)
        {
            throw new InvalidOperationException("Target safety stock quantity must be greater than or equal to the reorder point quantity.");
        }

        if (targetStockQuantity <= targetSafetyStockQuantity)
        {
            throw new InvalidOperationException("Target stock quantity must be greater than the target safety stock quantity.");
        }

        if (sourceSafetyStockQuantity < 0m)
        {
            throw new InvalidOperationException("Source safety stock quantity cannot be negative.");
        }

        RuleCode = ruleCode;
        RuleName = ruleName;
        BranchId = branchId;
        TargetWarehouseId = targetWarehouseId;
        PreferredSourceWarehouseId = preferredSourceWarehouseId;
        ItemId = itemId;
        UnitOfMeasureCode = unitOfMeasureCode;
        ReorderPointQuantity = reorderPointQuantity;
        TargetSafetyStockQuantity = targetSafetyStockQuantity;
        TargetStockQuantity = targetStockQuantity;
        SourceSafetyStockQuantity = sourceSafetyStockQuantity;
        Priority = priority;
        EffectiveFromDate = effectiveFromDate;
        EffectiveToDate = effectiveToDate;
        Notes = notes;
    }

    private ReplenishmentRule()
    {
    }

    public void Submit(long actorUserId)
    {
        if (StatusId != WorkflowStatusSeedIds.Draft && StatusId != WorkflowStatusSeedIds.Returned)
        {
            throw new InvalidOperationException("Only draft or returned replenishment 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 replenishment rules can be approved.");
        }

        if (SubmittedByUserId == actorUserId)
        {
            throw new InvalidOperationException("Replenishment 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 replenishment rules can be activated.");
        }

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

    public bool AppliesTo(long branchId, DateOnly asOfDate, long? targetWarehouseId, long? itemId) =>
        IsActive
        && StatusId == WorkflowStatusSeedIds.Approved
        && BranchId == branchId
        && (!targetWarehouseId.HasValue || TargetWarehouseId == targetWarehouseId.Value)
        && (!itemId.HasValue || ItemId == itemId.Value)
        && asOfDate >= EffectiveFromDate
        && (!EffectiveToDate.HasValue || asOfDate <= EffectiveToDate.Value);
}
