package de.effigenix.application.production;

import de.effigenix.domain.production.*;
import de.effigenix.shared.common.Result;

import java.util.*;

public class RecipeCycleChecker {

    private final RecipeRepository recipeRepository;

    public RecipeCycleChecker(RecipeRepository recipeRepository) {
        this.recipeRepository = recipeRepository;
    }

    public Result<RecipeError, Void> check(String parentRecipeId, String subRecipeId) {
        if (parentRecipeId.equals(subRecipeId)) {
            return Result.failure(new RecipeError.CyclicDependencyDetected(
                    List.of(parentRecipeId, subRecipeId)));
        }

        var visited = new HashSet<String>();
        var stack = new ArrayDeque<List<String>>();
        stack.push(List.of(parentRecipeId, subRecipeId));

        while (!stack.isEmpty()) {
            var path = stack.pop();
            var currentId = path.get(path.size() - 1);

            if (!visited.add(currentId)) {
                continue;
            }

            Recipe recipe;
            switch (recipeRepository.findById(RecipeId.of(currentId))) {
                case Result.Failure(var err) ->
                    { return Result.failure(new RecipeError.RepositoryFailure(err.message())); }
                case Result.Success(var opt) -> {
                    if (opt.isEmpty()) {
                        continue;
                    }
                    recipe = opt.get();
                }
            }

            for (var ingredient : recipe.ingredients()) {
                var childSubRecipeId = ingredient.subRecipeId();
                if (childSubRecipeId == null) {
                    continue;
                }

                if (childSubRecipeId.equals(parentRecipeId)) {
                    var cyclePath = new ArrayList<>(path);
                    cyclePath.add(childSubRecipeId);
                    return Result.failure(new RecipeError.CyclicDependencyDetected(cyclePath));
                }

                if (!visited.contains(childSubRecipeId)) {
                    var newPath = new ArrayList<>(path);
                    newPath.add(childSubRecipeId);
                    stack.push(newPath);
                }
            }
        }

        return Result.success(null);
    }
}
