package de.effigenix.domain.production;

import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import de.effigenix.shared.common.Result;

/**
 * Fuzz test for the Recipe aggregate.
 *
 * Exercises create() with fuzzed drafts, then applies a random sequence of
 * state-mutating operations (addIngredient, addProductionStep, removeIngredient,
 * removeProductionStep, activate, archive).
 * Verifies that no input combination causes an unhandled exception –
 * all invalid inputs must be caught and returned as Result.Failure.
 *
 * Run: make fuzz  |  make fuzz/single TEST=RecipeFuzzTest
 */
class RecipeFuzzTest {

    private static final RecipeType[] RECIPE_TYPES = RecipeType.values();

    @FuzzTest(maxDuration = "5m")
    void fuzzRecipe(FuzzedDataProvider data) {
        var draft = new RecipeDraft(
                data.consumeString(200),
                data.consumeInt(0, 100),
                data.consumeBoolean() ? data.pickValue(RECIPE_TYPES) : null,
                data.consumeString(200),
                data.consumeInt(0, 300),
                data.consumeBoolean() ? data.consumeInt(-10, 400) : null,
                data.consumeString(30),
                data.consumeString(20),
                data.consumeString(40)
        );

        switch (Recipe.create(draft)) {
            case Result.Failure(var err) -> { }
            case Result.Success(var recipe) -> {
                int ops = data.consumeInt(1, 12);
                for (int i = 0; i < ops; i++) {
                    int op = data.consumeInt(0, 5);
                    switch (op) {
                        case 0 -> {
                            var ingredientDraft = consumeIngredientDraft(data);
                            if (ingredientDraft != null) recipe.addIngredient(ingredientDraft);
                        }
                        case 1 -> {
                            recipe.addProductionStep(new ProductionStepDraft(
                                    data.consumeInt(), data.consumeString(100),
                                    data.consumeBoolean() ? data.consumeInt() : null,
                                    data.consumeBoolean() ? data.consumeInt() : null
                            ));
                        }
                        case 2 -> recipe.activate();
                        case 3 -> recipe.archive();
                        case 4 -> {
                            if (!recipe.ingredients().isEmpty()) {
                                recipe.removeIngredient(recipe.ingredients().getFirst().id());
                            }
                        }
                        case 5 -> {
                            if (!recipe.productionSteps().isEmpty()) {
                                recipe.removeProductionStep(recipe.productionSteps().getFirst().stepNumber());
                            }
                        }
                    }
                }
            }
        }
    }

    private static IngredientDraft consumeIngredientDraft(FuzzedDataProvider data) {
        try {
            return new IngredientDraft(
                    data.consumeInt(),
                    data.consumeString(50),
                    data.consumeString(30),
                    data.consumeString(20),
                    data.consumeBoolean() ? data.consumeString(40) : null,
                    data.consumeBoolean()
            );
        } catch (IllegalArgumentException e) {
            return null;
        }
    }
}
