/* © SRSoftware 2025 */
package de.srsoftware.umbrella.messagebus.events;

import static de.srsoftware.umbrella.core.constants.Field.*;
import static java.util.Optional.*;

import de.srsoftware.tools.Diff;
import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.model.Translatable;
import de.srsoftware.umbrella.core.model.UmbrellaUser;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.json.JSONObject;

public abstract class Event<Payload extends Mappable> {

	public enum EventType {
		CREATE,
		MEMBER_ADDED,
		UPDATE,
		DELETE;
	}

	private final UmbrellaUser initiator;
	private final String module;
	private final Payload payload;
	private final EventType eventType;
	private final Map<String, Object> oldData;

	public Event(UmbrellaUser initiator, String module, Payload payload, EventType type){
		this.initiator = initiator;
		this.module    = module;
		this.payload   = payload;
		this.eventType = type;
		this.oldData   = null;
	}

	public Event(UmbrellaUser initiator, String module, Payload payload, Map<String, Object> oldData){
		this.initiator = initiator;
		this.module = module;
		this.payload = payload;
		this.eventType = EventType.UPDATE;
		this.oldData = oldData;
	}

	public abstract Collection<UmbrellaUser> audience();

	public abstract Translatable describe();

	private Map<String, Object> dropMarkdown(Map<String, Object> map) {
		var result = new HashMap<String, Object>();
		for (var entry : map.entrySet()){
			var v = entry.getValue();
			if (v instanceof Map<?,?> m && m.containsKey(RENDERED) && m.get(SOURCE) instanceof String s) v=s;
			result.put(entry.getKey(),v);
		}
		return result;
	}

	public Optional<String> diff(){
		return oldData == null ? empty() : of(Diff.MapDiff.diff(filter(oldData),filter(payload.toMap())));
	}


	public EventType eventType(){
		return eventType;
	}

	protected Map<String,Object> filter(Map<String,Object> map){
		return dropMarkdown(map);
	}

	public boolean isIntendedFor(UmbrellaUser user){
		return audience().contains(user);
	}

	public UmbrellaUser initiator(){
		return initiator;
	}

	public String json(){
		Class<?> clazz = payload.getClass();
		{ // get the highest superclass that is not object
			Class<?> parent = clazz.getSuperclass();

			while (parent != null && parent != Object.class) {
				clazz = parent;
				parent = clazz.getSuperclass();
			}
		}
		var map = Map.of(USER,initiator.toMap(),clazz.getSimpleName().toLowerCase(),payload.toMap());
		return new JSONObject(map).toString();
	}

	public String module(){
		return module;
	}

	protected Map<String, Object> oldData() {
		return oldData;
	}

	public Payload payload(){
		return payload;
	}

	public abstract Translatable subject();
}
