/* © SRSoftware 2025 */
package de.srsoftware.umbrella.core.model;

import static de.srsoftware.umbrella.core.constants.Constants.JSONARRAY;
import static de.srsoftware.umbrella.core.constants.Constants.JSONOBJECT;
import static de.srsoftware.umbrella.core.constants.Field.ID;
import static de.srsoftware.umbrella.core.constants.Field.RECEIVERS;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.core.model.Translatable.t;
import static java.text.MessageFormat.format;

import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.json.JSONArray;
import org.json.JSONObject;

public class Envelope<T extends Message<?>> {
	private final T message;
	private final Set<User> receivers;
	private final long id;

	public Envelope(long id, T message, User receiver){
		this(id, message,new HashSet<>(Set.of(receiver)));
	}

	public Envelope(long id, T message, Collection<? extends User> receivers) {
		this.message = message;
		this.receivers = new HashSet<>(receivers);
		this.id = id;
	}

	/**
	 * TODO: this is legacy, move to legacy module!
	 * @param json
	 * @return
	 * @throws UmbrellaException
	 */
	public static Envelope<TranslatedMessage> from(JSONObject json) throws UmbrellaException {
		if (!json.has(RECEIVERS)) throw missingField(RECEIVERS);
		var id = json.has(ID) && json.get(ID) instanceof Number n ? n.longValue() : 0;
		var message = TranslatedMessage.from(json);
		var obj = json.get(RECEIVERS);
		if (obj instanceof JSONObject) obj = new JSONArray(List.of(obj));
		if (!(obj instanceof JSONArray receiverList)) throw invalidField(RECEIVERS, t(JSONARRAY));
		var receivers = new HashSet<User>();
		for (var o : receiverList){
			if (!(o instanceof JSONObject receiverData)) throw invalidField("entries of "+ RECEIVERS, t(JSONOBJECT));
			receivers.add(User.of(receiverData));
		}
		return new Envelope<>(id, message, receivers);
	}

	@Override
	public final boolean equals(Object o) {
		if (!(o instanceof Envelope<?> envelope)) return false;
		return message.equals(envelope.message) && id == envelope.id;
	}

	@Override
	public int hashCode() {
		return message.hashCode();
	}

	public long id(){
		return id;
	}

	public boolean isFor(User receiver) {
		return receivers.stream().anyMatch(r -> r.equals(receiver));
	}

	public T message(){
		return message;
	}

	public Set<User> receivers(){
		return receivers;
	}

	@Override
	public String toString() {
		return format("{0} (to: {1}), subject: {2}",getClass().getSimpleName(),receivers.stream().map(User::email).map(EmailAddress::toString).collect(Collectors.joining(", ")),message.subject());
	}
}
