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

import static de.srsoftware.tools.Optionals.nullable;
import static de.srsoftware.umbrella.core.Util.mapMarkdown;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.model.Status.PREDEFINED;

import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.constants.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import org.json.JSONObject;

public class Project implements Mappable {
	private final Map<Long,Member> members;
	private final Collection<Status> allowedStates;
	private boolean showClosed;
	private Long companyId;
	private int status;
	private String name;
	private final long id;
	private String description;
	private final Set<String> dirtyFields = new HashSet<>();
	private final Map<String,String> tagColors = new HashMap<>();

	public Project(long id, String name, String description, int status, Long companyId, boolean showClosed, Map<Long,Member> members, Collection<Status> allowedStates) {
		this.id = id;
		this.name = name;
		this.description = description;
		this.status = status;
		this.companyId = companyId;
		this.showClosed = showClosed;
		this.members = members;
		this.allowedStates = new ArrayList<>(allowedStates);
	}

	public Collection<Status> allowedStates(){
		return allowedStates;
	}

	public Project clean() {
		dirtyFields.clear();
		return this;
	}

	public Project clean(String field){
		dirtyFields.remove(field);
		return this;
	}

	public Optional<Long> companyId(){
		return nullable(companyId);
	}

	public String description(){
		return description;
	}

	public Project dirty(String field){
		dirtyFields.add(field);
		return this;
	}

	public boolean hasMember(UmbrellaUser user) {
		return members.containsKey(user.id());
	}

	public long id(){
		return id;
	}

	public boolean isDirty() {
		return !dirtyFields.isEmpty();
	}

	public boolean isDirty(String field){
		return dirtyFields.contains(field);
	}


	public Map<Long,Member> members(){
			return members;
	}

	public String name(){
		return name;
	}

	public static Project of(ResultSet rs) throws SQLException {
		var companyId = rs.getLong(COMPANY_ID);
		return new Project(rs.getLong(ID),rs.getString(NAME),rs.getString(DESCRIPTION),rs.getInt(STATUS),companyId == 0 ? null : companyId,rs.getBoolean(SHOW_CLOSED),new HashMap<>(),PREDEFINED);
	}

	public Project patch(JSONObject json) {
		for (var key : json.keySet()){
			switch (key){
				case COMPANY_ID:  companyId   = json.getLong(COMPANY_ID); break;
				case DESCRIPTION: description = json.getString(key); break;
				case NAME:        name        = json.getString(key); break;
				case SHOW_CLOSED: showClosed  = json.getBoolean(SHOW_CLOSED); break;
				case STATUS:      status      = json.get(key) instanceof Number state ? json.getInt(key) : Status.matching(json.getString(key)).code(); break;
				case TAG_COLORS:
					tagColors.clear();
					json.getJSONObject(TAG_COLORS).toMap().forEach((k,v) -> tagColors.put(k,v.toString()));
					break;
				default: key = null;
			}
			if (key != null) dirtyFields.add(key);
		}
		return this;
	}

	public boolean showClosed(){
		return showClosed;
	}

	public int status(){
		return status;
	}

	public Map<String, String> tagColors() {
		return tagColors;
	}

	@Override
	public Map<String, Object> toMap() {
		var map = new HashMap<String, Object>();
		var memberMap = new HashMap<Long,Map<String,Object>>();
		if (members != null) for (var entry : members.entrySet()){
			memberMap.put(entry.getKey(),entry.getValue().toMap());
		}
		map.put(ID,id);
		map.put(NAME,name);
		map.put(DESCRIPTION,mapMarkdown(description));
		map.put(STATUS,status);
		map.put(COMPANY_ID,companyId);
		map.put(SHOW_CLOSED,showClosed);
		map.put(MEMBERS,memberMap);
		var stateMap = new HashMap<Integer,String>();
		for (var state : allowedStates) stateMap.put(state.code(),state.name());
		map.put(Field.ALLOWED_STATES,stateMap);
		map.put(TAG_COLORS,tagColors);
		return map;
	}

	@Override
	public String toString() {
		return name();
	}
}
