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

namespace CashLineERP.Domain.Finance;

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

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

    public long? BranchId { get; private set; }

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

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

    public DateOnly AcquisitionDate { get; private set; }

    public DateOnly CapitalizationDate { get; private set; }

    public decimal AcquisitionCostAmount { get; private set; }

    public int UsefulLifeMonths { get; private set; }

    public decimal SalvageValueAmount { get; private set; }

    public string DepreciationMethodCode { get; private set; } = "STRAIGHT_LINE";

    public long AssetAccountId { get; private set; }

    public long ClearingAccountId { get; private set; }

    public long DepreciationExpenseAccountId { get; private set; }

    public long AccumulatedDepreciationAccountId { get; private set; }

    public string? Notes { get; private set; }

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

    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? CapitalizedAtUtc { get; private set; }

    public long? CapitalizedByUserId { get; private set; }

    public long? GeneralLedgerBatchId { get; private set; }

    public FixedAsset(
        string assetNumber,
        long? branchId,
        string assetClassCode,
        string assetName,
        DateOnly acquisitionDate,
        DateOnly capitalizationDate,
        decimal acquisitionCostAmount,
        int usefulLifeMonths,
        decimal salvageValueAmount,
        string depreciationMethodCode,
        long assetAccountId,
        long clearingAccountId,
        long depreciationExpenseAccountId,
        long accumulatedDepreciationAccountId,
        string? notes)
    {
        if (string.IsNullOrWhiteSpace(assetClassCode))
        {
            throw new InvalidOperationException("Fixed asset class code is required.");
        }

        if (string.IsNullOrWhiteSpace(assetName))
        {
            throw new InvalidOperationException("Fixed asset name is required.");
        }

        if (acquisitionCostAmount <= 0m)
        {
            throw new InvalidOperationException("Fixed asset acquisition cost must be greater than zero.");
        }

        if (usefulLifeMonths <= 0)
        {
            throw new InvalidOperationException("Fixed asset useful life must be greater than zero.");
        }

        if (salvageValueAmount < 0m || salvageValueAmount >= acquisitionCostAmount)
        {
            throw new InvalidOperationException("Fixed asset salvage value must be zero or a positive amount below acquisition cost.");
        }

        if (!string.Equals(depreciationMethodCode?.Trim(), "STRAIGHT_LINE", StringComparison.OrdinalIgnoreCase))
        {
            throw new InvalidOperationException("Fixed asset baseline currently supports only STRAIGHT_LINE depreciation.");
        }

        AssetNumber = assetNumber;
        BranchId = branchId;
        AssetClassCode = assetClassCode.Trim().ToUpperInvariant();
        AssetName = assetName.Trim();
        AcquisitionDate = acquisitionDate;
        CapitalizationDate = capitalizationDate;
        AcquisitionCostAmount = acquisitionCostAmount;
        UsefulLifeMonths = usefulLifeMonths;
        SalvageValueAmount = salvageValueAmount;
        DepreciationMethodCode = "STRAIGHT_LINE";
        AssetAccountId = assetAccountId;
        ClearingAccountId = clearingAccountId;
        DepreciationExpenseAccountId = depreciationExpenseAccountId;
        AccumulatedDepreciationAccountId = accumulatedDepreciationAccountId;
        Notes = string.IsNullOrWhiteSpace(notes) ? null : notes.Trim();
    }

    private FixedAsset()
    {
    }

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

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

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

    public void Capitalize(long actorUserId, long generalLedgerBatchId)
    {
        if (StatusId == WorkflowStatusSeedIds.Posted)
        {
            GeneralLedgerBatchId ??= generalLedgerBatchId;
            return;
        }

        if (StatusId != WorkflowStatusSeedIds.Approved)
        {
            throw new InvalidOperationException("Fixed asset must be approved before capitalization.");
        }

        StatusId = WorkflowStatusSeedIds.Posted;
        GeneralLedgerBatchId = generalLedgerBatchId;
        CapitalizedAtUtc = DateTime.UtcNow;
        CapitalizedByUserId = actorUserId;
        Touch(actorUserId);
    }
}
