package peergos.shared.user;

import peergos.shared.cbor.*;
import peergos.shared.corenode.*;
import peergos.shared.crypto.asymmetric.*;

import java.util.*;
import java.util.stream.*;

public class TofuKeyStore implements Cborable {

    private final Map<String, List<UserPublicKeyLink>> chains;
    private final Map<PublicSigningKey, String> reverseLookup = new HashMap<>();

    public TofuKeyStore(Map<String, List<UserPublicKeyLink>> chains) {
        this.chains = chains;
        updateReverseLookup();
    }

    public TofuKeyStore() {
        this(new HashMap<>());
    }

    public Optional<PublicSigningKey> getPublicKey(String username) {
        List<UserPublicKeyLink> chain = chains.get(username);
        if (chain == null)
            return Optional.empty();
        return Optional.of(chain.get(chain.size() - 1).owner);
    }

    public Optional<String> getUsername(PublicSigningKey signer) {
        String name = reverseLookup.get(signer);
        if (name == null)
            return Optional.empty();
        return Optional.of(name);
    }

    public List<UserPublicKeyLink> getChain(String username) {
        return chains.getOrDefault(username, Collections.emptyList());
    }

    public void updateChain(String username, List<UserPublicKeyLink> tail) {
        UserPublicKeyLink.validChain(tail, username);

        List<UserPublicKeyLink> existing = getChain(username);
        List<UserPublicKeyLink> merged = UserPublicKeyLink.merge(existing, tail);
        chains.put(username, merged);
        PublicSigningKey owner = tail.get(tail.size() - 1).owner;
        reverseLookup.put(owner, username);
    }

    private void updateReverseLookup() {
        reverseLookup.clear();
        reverseLookup.putAll(chains.entrySet().stream()
                .collect(Collectors.toMap(
                        e -> e.getValue().get(e.getValue().size() - 1).owner,
                        e -> e.getKey())));
    }

    @Override
    public CborObject toCbor() {
        SortedMap<CborObject, CborObject> state = new TreeMap<>();
        chains.forEach((name, chain) -> state.put(new CborObject.CborString(name),
                new CborObject.CborList(chain.stream()
                        .map(link -> link.toCbor())
                        .collect(Collectors.toList()))));
        return new CborObject.CborMap(state);
    }

    public static TofuKeyStore fromCbor(CborObject cbor) {
        if (! (cbor instanceof CborObject.CborMap))
            throw new IllegalStateException("Invalid cbor for Tofu key store: " + cbor);

        Map<String, List<UserPublicKeyLink>> chains = new HashMap<>();
        SortedMap<CborObject, CborObject> values = ((CborObject.CborMap) cbor).values;
        for (CborObject key: values.keySet()) {
            if (key instanceof CborObject.CborString) {
                String name = ((CborObject.CborString) key).value;
                CborObject value = values.get(key);
                if (value instanceof CborObject.CborList) {
                    List<UserPublicKeyLink> chain = ((CborObject.CborList) value).value.stream()
                            .map(UserPublicKeyLink::fromCbor)
                            .collect(Collectors.toList());
                    UserPublicKeyLink.validChain(chain, name);
                    chains.put(name, chain);
                } else throw new IllegalStateException("Invalid value in Tofu key store map: " + value);
            } else throw new IllegalStateException("Invalid key in Tofu key store map: " + key);
        }
        return new TofuKeyStore(chains);
    }
}

@@ -972,71 +972,8 @@
     
-if (name == null)%0A            return Optional.empty();%0A        
 retu
@@ -986,16 +986,24 @@
 ional.of
+Nullable
 (name);%0A

clean up