using CashLineERP.Contracts.Finance;

namespace CashLineERP.Application.Finance;

public sealed class FixedAssetService : IFixedAssetService
{
    private readonly IFixedAssetRepository _repository;

    public FixedAssetService(IFixedAssetRepository repository)
    {
        _repository = repository;
    }

    public IReadOnlyCollection<FixedAssetResponse> GetFixedAssets() =>
        _repository.GetFixedAssets().Select(Map).ToArray();

    public FixedAssetResponse? GetFixedAsset(long fixedAssetId)
    {
        var asset = _repository.GetFixedAsset(fixedAssetId);
        return asset is null ? null : Map(asset);
    }

    public FixedAssetResponse CreateFixedAsset(CreateFixedAssetRequest request) =>
        Map(_repository.Create(new CreateFixedAssetCommand(
            request.BranchId,
            request.AssetClassCode,
            request.AssetName,
            request.AcquisitionDate,
            request.CapitalizationDate,
            request.AcquisitionCostAmount,
            request.UsefulLifeMonths,
            request.SalvageValueAmount,
            request.DepreciationMethodCode,
            request.AssetAccountId,
            request.ClearingAccountId,
            request.DepreciationExpenseAccountId,
            request.AccumulatedDepreciationAccountId,
            request.Notes)));

    public FixedAssetResponse SubmitFixedAsset(long fixedAssetId, long actorUserId) =>
        Map(_repository.Submit(fixedAssetId, actorUserId));

    public FixedAssetResponse ApproveFixedAsset(long fixedAssetId, long actorUserId) =>
        Map(_repository.Approve(fixedAssetId, actorUserId));

    public FixedAssetResponse CapitalizeFixedAsset(long fixedAssetId, long actorUserId) =>
        Map(_repository.Capitalize(fixedAssetId, actorUserId));

    public IReadOnlyCollection<FixedAssetDepreciationScheduleRowResponse> GetDepreciationSchedule(long fixedAssetId)
    {
        var asset = _repository.GetFixedAsset(fixedAssetId)
            ?? throw new InvalidOperationException("Fixed asset was not found.");

        var depreciableBase = asset.AcquisitionCostAmount - asset.SalvageValueAmount;
        var roundedMonthlyAmount = Math.Round(depreciableBase / asset.UsefulLifeMonths, 2, MidpointRounding.AwayFromZero);
        var accumulatedAmount = 0m;
        var rows = new List<FixedAssetDepreciationScheduleRowResponse>(asset.UsefulLifeMonths);

        for (var monthIndex = 0; monthIndex < asset.UsefulLifeMonths; monthIndex++)
        {
            var depreciationDate = asset.CapitalizationDate.AddMonths(monthIndex);
            var depreciationAmount = monthIndex == asset.UsefulLifeMonths - 1
                ? depreciableBase - accumulatedAmount
                : roundedMonthlyAmount;

            accumulatedAmount += depreciationAmount;
            var netBookValueAmount = asset.AcquisitionCostAmount - accumulatedAmount;

            rows.Add(new FixedAssetDepreciationScheduleRowResponse(
                monthIndex + 1,
                depreciationDate,
                depreciationAmount,
                accumulatedAmount,
                netBookValueAmount));
        }

        return rows;
    }

    private static FixedAssetResponse Map(FixedAssetDetailView asset) =>
        new(
            asset.FixedAssetId,
            asset.AssetNumber,
            asset.BranchId,
            asset.AssetClassCode,
            asset.AssetName,
            asset.AcquisitionDate,
            asset.CapitalizationDate,
            asset.AcquisitionCostAmount,
            asset.UsefulLifeMonths,
            asset.SalvageValueAmount,
            asset.DepreciationMethodCode,
            asset.AssetAccountId,
            asset.ClearingAccountId,
            asset.DepreciationExpenseAccountId,
            asset.AccumulatedDepreciationAccountId,
            asset.Notes,
            asset.StatusCode,
            asset.SubmittedAtUtc,
            asset.SubmittedByUserId,
            asset.ApprovedAtUtc,
            asset.ApprovedByUserId,
            asset.CapitalizedAtUtc,
            asset.CapitalizedByUserId,
            asset.GeneralLedgerBatchId);
}
