package de.effigenix.domain.production;

import de.effigenix.domain.masterdata.article.ArticleId;
import de.effigenix.shared.common.Quantity;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.common.UnitOfMeasure;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;

public class Consumption {

    private final ConsumptionId id;
    private final BatchId inputBatchId;
    private final ArticleId articleId;
    private final Quantity quantityUsed;
    private final OffsetDateTime consumedAt;

    private Consumption(ConsumptionId id, BatchId inputBatchId, ArticleId articleId,
                        Quantity quantityUsed, OffsetDateTime consumedAt) {
        this.id = id;
        this.inputBatchId = inputBatchId;
        this.articleId = articleId;
        this.quantityUsed = quantityUsed;
        this.consumedAt = consumedAt;
    }

    public static Result<BatchError, Consumption> create(ConsumptionDraft draft) {
        if (draft.inputBatchId() == null || draft.inputBatchId().isBlank()) {
            return Result.failure(new BatchError.ValidationFailure("inputBatchId must not be blank"));
        }
        if (draft.articleId() == null || draft.articleId().isBlank()) {
            return Result.failure(new BatchError.ValidationFailure("articleId must not be blank"));
        }

        if (draft.quantityUsed() == null || draft.quantityUsed().isBlank()) {
            return Result.failure(new BatchError.InvalidConsumptionQuantity("quantityUsed must not be blank"));
        }
        if (draft.quantityUnit() == null || draft.quantityUnit().isBlank()) {
            return Result.failure(new BatchError.InvalidConsumptionQuantity("quantityUnit must not be blank"));
        }

        Quantity quantity;
        try {
            var amount = new BigDecimal(draft.quantityUsed());
            var uom = UnitOfMeasure.valueOf(draft.quantityUnit());
            switch (Quantity.of(amount, uom)) {
                case Result.Failure(var err) -> {
                    return Result.failure(new BatchError.InvalidConsumptionQuantity(err.toString()));
                }
                case Result.Success(var qty) -> quantity = qty;
            }
        } catch (NumberFormatException e) {
            return Result.failure(new BatchError.InvalidConsumptionQuantity(
                    "Invalid amount format: " + draft.quantityUsed()));
        } catch (IllegalArgumentException e) {
            return Result.failure(new BatchError.InvalidConsumptionQuantity(
                    "Invalid unit: " + draft.quantityUnit()));
        }

        return Result.success(new Consumption(
                ConsumptionId.generate(),
                BatchId.of(draft.inputBatchId()),
                ArticleId.of(draft.articleId()),
                quantity,
                OffsetDateTime.now(ZoneOffset.UTC)
        ));
    }

    public static Consumption reconstitute(ConsumptionId id, BatchId inputBatchId, ArticleId articleId,
                                           Quantity quantityUsed, OffsetDateTime consumedAt) {
        return new Consumption(id, inputBatchId, articleId, quantityUsed, consumedAt);
    }

    public ConsumptionId id() { return id; }
    public BatchId inputBatchId() { return inputBatchId; }
    public ArticleId articleId() { return articleId; }
    public Quantity quantityUsed() { return quantityUsed; }
    public OffsetDateTime consumedAt() { return consumedAt; }
}
