package click.kamil.springstatemachineexporter.analysis.index;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public final class PolymorphicAccessorIndex {

    private static final PolymorphicAccessorIndex EMPTY = new PolymorphicAccessorIndex(Map.of(), 0);

    private final Map<String, Map<String, PolymorphicAccessorEntry>> byOwnerAndMethod;
    private final int entryCount;

    public PolymorphicAccessorIndex(
            Map<String, Map<String, PolymorphicAccessorEntry>> byOwnerAndMethod,
            int entryCount) {
        this.byOwnerAndMethod = Collections.unmodifiableMap(deepCopy(byOwnerAndMethod));
        this.entryCount = entryCount;
    }

    public static PolymorphicAccessorIndex empty() {
        return EMPTY;
    }

    public Optional<PolymorphicAccessorEntry> lookup(String ownerFqn, String methodName) {
        if (ownerFqn == null || methodName == null) {
            return Optional.empty();
        }
        Map<String, PolymorphicAccessorEntry> methods = byOwnerAndMethod.get(ownerFqn);
        if (methods == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(methods.get(methodName));
    }

    public Optional<AccessorSummary> representativeImplAccessor(String ownerFqn, String methodName) {
        return lookup(ownerFqn, methodName)
                .flatMap(PolymorphicAccessorEntry::representativeImplAccessor);
    }

    public Optional<List<String>> widenedReturnConstants(String ownerFqn, String methodName) {
        return lookup(ownerFqn, methodName)
                .filter(PolymorphicAccessorEntry::staticallyComplete)
                .map(PolymorphicAccessorEntry::widenedReturnConstants)
                .filter(values -> !values.isEmpty());
    }

    public int entryCount() {
        return entryCount;
    }

    private static Map<String, Map<String, PolymorphicAccessorEntry>> deepCopy(
            Map<String, Map<String, PolymorphicAccessorEntry>> source) {
        Map<String, Map<String, PolymorphicAccessorEntry>> copy = new HashMap<>();
        for (Map.Entry<String, Map<String, PolymorphicAccessorEntry>> entry : source.entrySet()) {
            copy.put(entry.getKey(), Map.copyOf(entry.getValue()));
        }
        return copy;
    }
}
