import dev.miyomiyo.navi.api.*;
import dev.miyomiyo.navi.client.NaviClient;
import java.util.List;
import java.util.function.Consumer;

/** Load this adapter only after the loader confirms mod id "navi" is installed. */
public final class NaviExample {
    private Navigation navigation;
    private final Consumer<NavigationUpdate> publish;

    // publish must enqueue persistence/UI work, not call back into mutating Navi methods.
    public NaviExample(Consumer<NavigationUpdate> publish) {
        this.publish = publish;
    }

    // Call every method on the Minecraft client thread, after joining a world.
    public NavigationResult<Navigation> start() {
        if (navigation != null) return new NavigationResult.Failure<>("BUSY", "Already navigating");
        var request = NavigationRequest.builder(new NavigationRequest.Location(
                WorldPosition.block("minecraft:overworld", 100, 64, 200)))
                .name("Home").build();
        var result = NaviClient.navigation().start("example_mod",request);
        if (result instanceof NavigationResult.Success<Navigation> accepted) {
            navigation = accepted.value();
            navigation.observe(update -> {
                if (accepted.value() != navigation) return;
                if (update.terminal()) navigation = null;
                publish.accept(update);
            });
        }
        return result;
    }

    public NavigationResult<NavigationResult.Acknowledged> changeWaypoints(
            List<NavigationRequest.Waypoint> waypoints) {
        if (navigation == null) return new NavigationResult.Failure<>("INVALID_NAVIGATION", "No owned navigation");
        var read = navigation.read();
        if (read instanceof NavigationResult.Failure<NavigationSnapshot> failure)
            return new NavigationResult.Failure<>(failure.code(), failure.message());
        var snapshot = ((NavigationResult.Success<NavigationSnapshot>) read).value();
        var change = NavigationRefinement.builder().waypoints(waypoints);
        snapshot.tracking().ifPresent(t -> change.observedTargetRevision(t.revision()));
        return navigation.refine(snapshot.revision(), change.build());
    }

    public NavigationResult<NavigationResult.Acknowledged> stop() {
        if (navigation == null) return new NavigationResult.Failure<>("INVALID_NAVIGATION", "No owned navigation");
        // The observer receives the terminal update and releases our local navigation.
        return navigation.stop();
    }
}
