package dev.photosync.core.provider;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * The set of backends this build knows about, in the order the settings screen
 * offers them.
 *
 * <p>An instance rather than a static registry: it is created once by
 * {@code PhotoSync} and handed to whoever needs it, which keeps the wiring
 * visible and the whole thing constructible in a test.
 */
public final class ProviderCatalog {

    private final Map<ProviderId, ProviderFactory> factories;
    private final ProviderId preferred;

    public ProviderCatalog(List<ProviderFactory> factories) {
        if (factories.isEmpty()) {
            throw new IllegalArgumentException("A catalog needs at least one provider");
        }
        Map<ProviderId, ProviderFactory> byId = new LinkedHashMap<>();
        for (ProviderFactory factory : factories) {
            ProviderFactory clash = byId.put(factory.descriptor().id(), factory);
            if (clash != null) {
                throw new IllegalArgumentException("Duplicate provider id: " + factory.descriptor().id());
            }
        }
        this.factories = Map.copyOf(byId);
        this.preferred = factories.get(0).descriptor().id();
    }

    /** Used as the default in a fresh config, and as the fallback for an unknown id. */
    public ProviderId preferred() {
        return preferred;
    }

    public List<ProviderDescriptor> descriptors() {
        return factories.values().stream().map(ProviderFactory::descriptor).toList();
    }

    public Optional<ProviderDescriptor> describe(ProviderId id) {
        return Optional.ofNullable(factories.get(id)).map(ProviderFactory::descriptor);
    }

    /**
     * Connects using the named provider, falling back to {@link #preferred()}
     * when the config names one this build does not have -- which happens when a
     * player downgrades the mod, and is not worth crashing over.
     */
    public PhotoProvider connect(ProviderId id, ProviderConnection connection) {
        ProviderFactory factory = factories.getOrDefault(id, factories.get(preferred));
        return factory.connect(connection);
    }
}
