package org.asamk.signal.storage.protocol;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

public class RecipientStore {

    @JsonProperty("recipientStore")
    @JsonDeserialize(using = RecipientStoreDeserializer.class)
    @JsonSerialize(using = RecipientStoreSerializer.class)
    private final Set<SignalServiceAddress> addresses = new HashSet<>();

    public RecipientStore() {
    }

    public SignalServiceAddress resolveServiceAddress(SignalServiceAddress serviceAddress) {
        if (addresses.contains(serviceAddress)) {
            // If the Set already contains the exact address with UUID and Number,
            // we can just return it here.
            return serviceAddress;
        }

        for (SignalServiceAddress address : addresses) {
            if (address.matches(serviceAddress)) {
                return address;
            }
        }

        if (serviceAddress.getNumber().isPresent() && serviceAddress.getUuid().isPresent()) {
            addresses.add(serviceAddress);
        }

        return serviceAddress;
    }

    public static class RecipientStoreDeserializer extends JsonDeserializer<Set<SignalServiceAddress>> {

        @Override
        public Set<SignalServiceAddress> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
            JsonNode node = jsonParser.getCodec().readTree(jsonParser);

            Set<SignalServiceAddress> addresses = new HashSet<>();

            if (node.isArray()) {
                for (JsonNode recipient : node) {
                    String recipientName = recipient.get("name").asText();
                    UUID uuid = UuidUtil.parseOrThrow(recipient.get("uuid").asText());
                    final SignalServiceAddress serviceAddress = new SignalServiceAddress(uuid, recipientName);
                    addresses.add(serviceAddress);
                }
            }

            return addresses;
        }
    }

    public static class RecipientStoreSerializer extends JsonSerializer<Set<SignalServiceAddress>> {

        @Override
        public void serialize(Set<SignalServiceAddress> addresses, JsonGenerator json, SerializerProvider serializerProvider) throws IOException {
            json.writeStartArray();
            for (SignalServiceAddress address : addresses) {
                json.writeStartObject();
                json.writeStringField("name", address.getNumber().get());
                json.writeStringField("uuid", address.getUuid().get().toString());
                json.writeEndObject();
            }
            json.writeEndArray();
        }
    }
}