package de.effigenix.domain.production;

import de.effigenix.shared.common.Quantity;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.common.UnitOfMeasure;

import java.math.BigDecimal;

/**
 * Child entity of Recipe representing a single ingredient.
 *
 * Invariants:
 * 1. Position must be > 0
 * 2. ArticleId must not be blank
 * 3. Quantity must be positive
 */
public class Ingredient {

    private final IngredientId id;
    private final int position;
    private final String articleId;
    private final Quantity quantity;
    private final String subRecipeId;
    private final boolean substitutable;

    private Ingredient(IngredientId id, int position, String articleId,
                       Quantity quantity, String subRecipeId, boolean substitutable) {
        this.id = id;
        this.position = position;
        this.articleId = articleId;
        this.quantity = quantity;
        this.subRecipeId = subRecipeId;
        this.substitutable = substitutable;
    }

    public static Result<RecipeError, Ingredient> create(IngredientDraft draft) {
        if (draft.position() <= 0) {
            return Result.failure(new RecipeError.ValidationFailure(
                    "Ingredient position must be > 0, was: " + draft.position()));
        }

        if (draft.articleId() == null || draft.articleId().isBlank()) {
            return Result.failure(new RecipeError.ValidationFailure("Ingredient articleId must not be blank"));
        }

        Quantity quantity;
        try {
            BigDecimal amount = new BigDecimal(draft.quantity());
            UnitOfMeasure uom;
            try {
                uom = UnitOfMeasure.valueOf(draft.uom());
            } catch (IllegalArgumentException e) {
                return Result.failure(new RecipeError.ValidationFailure("Invalid unit of measure: " + draft.uom()));
            }
            switch (Quantity.of(amount, uom)) {
                case Result.Failure(var err) -> {
                    return Result.failure(new RecipeError.ValidationFailure(err.message()));
                }
                case Result.Success(var val) -> quantity = val;
            }
        } catch (NumberFormatException e) {
            return Result.failure(new RecipeError.ValidationFailure("Invalid ingredient quantity: " + e.getMessage()));
        }

        return Result.success(new Ingredient(
                IngredientId.generate(), draft.position(), draft.articleId(),
                quantity, draft.subRecipeId(), draft.substitutable()
        ));
    }

    public static Ingredient reconstitute(IngredientId id, int position, String articleId,
                                           Quantity quantity, String subRecipeId, boolean substitutable) {
        return new Ingredient(id, position, articleId, quantity, subRecipeId, substitutable);
    }

    public IngredientId id() { return id; }
    public int position() { return position; }
    public String articleId() { return articleId; }
    public Quantity quantity() { return quantity; }
    public String subRecipeId() { return subRecipeId; }
    public boolean substitutable() { return substitutable; }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (!(obj instanceof Ingredient other)) return false;
        return id.equals(other.id);
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }
}
