package leigh.chain.directory;

import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;

import java.io.Serial;
import java.io.Serializable;
import java.time.Instant;
import java.time.temporal.ChronoUnit;

/**
 * This class contains the logic necessary to replay a transaction ledger and populate a {@link DirectoryStore} object
 * with the current state of the directory.
 *
 * @author Alex Leigh
 */
public class DirectoryStateMachine implements Serializable {
    @Serial
    private static final long serialVersionUID = 0L;
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(DirectoryStateMachine.class);
    private final DirectoryStore store;

    public DirectoryStore getStore() {
        return store;
    }

    public DirectoryStateMachine(DirectoryStore store) {
        this.store = store;
    }

    /**
     * Process a name claim on the behalf of an address.
     * <p>
     * validityDays must be >=1 and <= 6*31. when is a trusted value.
     */
    public boolean processNameClaim(String address, String name,
                            int validityDays, String serviceUrl, Instant when) {

        logger.info("Processing name claim. [address: {}] [name: {}] [valid: {}] [service: {}]",
                address, name, validityDays, serviceUrl);

        if (validityDays > 6 * 31 || validityDays < 1) return false;

        Identity existing = store.getIdentityByName(name, when);

        if (existing != null) {
            String existingAddress = existing.getAddress();

            if (!existingAddress.equals(address)) {
                logger.info("Name already claimed. [claimAddr: {}] [existingAddr: {}] [name: {}]",
                        address,existingAddress, name);
                return false;
            } else {
                // This claimant already has an existing valid claim. Just update the expiry time and service URL.
                // TODO: Set the serviceUrl here
                Instant newExpiry = when.plus(validityDays, ChronoUnit.DAYS);
                existing.setNameClaimExpire(newExpiry);
                existing.setServiceUrl(serviceUrl);
                return true;
            }
        }

        // No existing claim, so we straight up get to create one.
        Identity id = store.getIdentityByAddr(address);

        if (id == null) {
            id = new Identity();
            id.setAddress(address);
        } else {
            // If we already have an ID there must be an old name entry
            store.removeName(id.getName());
        }

        id.setName(name);
        id.setServiceUrl(serviceUrl);

        store.putIdentity(id);

        // TODO Set serviceUrl
        id.setNameClaimExpire(when.plus(validityDays, ChronoUnit.DAYS));

        return true;
    }

    @Override
    public String toString() {
        return "DirectoryStateMachine{" +
                "store=" + store +
                '}';
    }
}
