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

import static de.srsoftware.tools.Optionals.emptyIfNull;
import static de.srsoftware.umbrella.core.constants.Field.*;
import static de.srsoftware.umbrella.core.constants.Module.COMPANY;
import static java.util.Map.entry;

import de.srsoftware.tools.Mappable;
import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import org.json.JSONException;
import org.json.JSONObject;

public class Company implements Mappable, Owner {
	private final long id;
	private String name, address, court, taxId, phone, decimalSeparator, thousandsSeparator,  customerNumberPrefix, currency, email, bankAccount;;
	private long lastCustomerNumber;
	private int decimals;
	private final Set<String> dirtyFields = new HashSet<>();
	private final Map<Long,UmbrellaUser> members;

	private Company(long id){
		this(id,null,null,null,null,null,null,null,0,2,null,null,null,null);
	}

	public Company(long id, String name, String address, String court, String taxId, String phone, String decimalSep, String thousandsSep, long lastCustomerNumber, int decimals, String customerNumberPrefix, String currency, String email, String bankAccount){
		this.id = id;
		this.name = name;
		this.address = address;
		this.court = court;
		this.taxId = taxId;
		this.phone = phone;
		this.decimalSeparator = decimalSep;
		this.thousandsSeparator = thousandsSep;
		this.lastCustomerNumber = lastCustomerNumber;
		this.decimals = decimals;
		this.customerNumberPrefix = customerNumberPrefix;
		this.currency = currency;
		this.email = email;
		this.bankAccount = bankAccount;
		this.members = new HashMap<>();
	}

	public String address(){
		return  address;
	}

	public String bankAccount() {
		return bankAccount;
	}

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

	public String court() {
		return court;
	}

	public String currency() {
		return currency;
	}

	public String customerNumberPrefix() {
		return customerNumberPrefix;
	}

	public int decimals() {
		return decimals;
	}

	public String decimalSeparator() {
		return decimalSeparator;
	}

	@Override
	public boolean equals(Object obj) {
		return obj instanceof Company c && c.id == id;
	}

	public String email() {
		return email;
	}

	@Override
	public long id() {
		return id;
	}

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

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

	public long lastCustomerNumber() {
		return lastCustomerNumber;
	}

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

	public String name(){
		return name;
	}

	public static Company of(JSONObject json) throws UmbrellaException {
		try {
			return new Company(json.getLong(ID)).patch(json).clean();
		} catch (JSONException e){
			throw new UmbrellaException(500,"Failed to convert JSON to Company!").causedBy(e);
		}
	}

	public static Company of(ResultSet json) throws UmbrellaException {
		try {
			var id  = json.getLong(ID);
			var name = json.getString(NAME);
			var address = json.getString(ADDRESS);
			var email = json.getString(EMAIL);
			var phone = json.getString(PHONE);
			var bankAccount = json.getString(BANK_ACCOUNT);
			var court = json.getString(COURT);
			var currency = json.getString(CURRENCY);
			var taxId = json.getString(TAX_NUMBER);
			var decimals = json.getInt(DECIMALS);
			var decimalSep = json.getString(DECIMAL_SEPARATOR);
			var thousandsSep = json.getString(THOUSANDS_SEPARATOR);
			var lastCustomerNumber = json.getLong(LAST_CUSTOMER_NUMBER);
			var customerNumberPrefix = json.getString(CUSTOMER_NUMBER_PREFIX);
			return new Company(id,name,address,court,taxId,phone,decimalSep,thousandsSep,lastCustomerNumber,decimals,customerNumberPrefix,currency,email,bankAccount);
		} catch (SQLException e){
			throw new UmbrellaException(500,"Failed to convert ResultSet to Company!").causedBy(e);
		}
	}

	public Company patch(JSONObject json) {
		for (var key : json.keySet()){
			switch (key){
				case NAME: name = json.getString(key); break;
				case ADDRESS: address = json.getString(key); break;
				case COURT: court = json.getString(key); break;
				case TAX_NUMBER: taxId = json.getString(key); break;
				case PHONE: phone = json.getString(key); break;
				case DECIMAL_SEPARATOR: decimalSeparator = json.getString(key); break;
				case THOUSANDS_SEPARATOR: thousandsSeparator = json.getString(key); break;
				case LAST_CUSTOMER_NUMBER: lastCustomerNumber = json.getLong(key); break;
				case DECIMALS: decimals = json.getInt(key); break;
				case CUSTOMER_NUMBER_PREFIX: customerNumberPrefix = json.getString(key); break;
				case CURRENCY: currency = json.getString(key); break;
				case EMAIL: email = json.getString(key); break;
				case BANK_ACCOUNT: bankAccount = json.getString(key); break;
				default: key = null;
			}
			if (key != null) dirtyFields.add(key);
		}

		return this;
	}

	public String phone() {
		return phone;
	}

	@Override
	public Owner resolve() {
		return this;
	}

	public String taxId() {
		return taxId;
	}

	public String thousandsSeparator() {
		return thousandsSeparator;
	}

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

	@Override
	public Map<String, Object> toMap() {
		var memberMap = new HashMap<Long,Map<String,Object>>();
		if (members != null) for (var entry : members.entrySet()){
			memberMap.put(entry.getKey(),entry.getValue().toMap());
		}
		return Map.ofEntries(
				entry(NAME,name),
				entry(ADDRESS,emptyIfNull(address)),
				entry(COURT,emptyIfNull(court)),
				entry(TAX_NUMBER,emptyIfNull(taxId)),
				entry(PHONE,emptyIfNull(phone)),
				entry(DECIMAL_SEPARATOR,emptyIfNull(decimalSeparator)),
				entry(THOUSANDS_SEPARATOR,emptyIfNull(thousandsSeparator)),
				entry(LAST_CUSTOMER_NUMBER,lastCustomerNumber),
				entry(DECIMALS,decimals),
				entry(CUSTOMER_NUMBER_PREFIX,emptyIfNull(customerNumberPrefix)),
				entry(CURRENCY,emptyIfNull(currency)),
				entry(EMAIL,emptyIfNull(email)),
				entry(BANK_ACCOUNT,emptyIfNull(bankAccount)),
				entry(MEMBERS,memberMap),
				entry(TYPE,type()),
				entry(ID,id)
			);
	}

	@Override
	public String type() {
		return COMPANY;
	}
}
