package com.parkingexchange.common;

public class AssertionConcern {

    protected AssertionConcern() {
        super();
    }

    protected void assertArgumentEquals(Object anObject1, Object anObject2, String aMessage) {
        if (!anObject1.equals(anObject2)) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentFalse(boolean aBoolean, String aMessage) {
        if (aBoolean) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentLength(String aString, int aMaximum, String aMessage) {
        int length = aString.trim().length();
        if (length > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentLength(String aString, int aMinimum, int aMaximum, String aMessage) {
        int length = aString.trim().length();
        if (length < aMinimum || length > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentNotEmpty(String aString, String aMessage) {
        if (aString == null || aString.trim().isEmpty()) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentNotEquals(Object anObject1, Object anObject2, String aMessage) {
        if (anObject1.equals(anObject2)) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentNotNull(Object anObject, String aMessage) {
        if (anObject == null) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentRange(double aValue, double aMinimum, double aMaximum, String aMessage) {
        if (aValue < aMinimum || aValue > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentRange(float aValue, float aMinimum, float aMaximum, String aMessage) {
        if (aValue < aMinimum || aValue > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentRange(int aValue, int aMinimum, int aMaximum, String aMessage) {
        if (aValue < aMinimum || aValue > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentRange(long aValue, long aMinimum, long aMaximum, String aMessage) {
        if (aValue < aMinimum || aValue > aMaximum) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertArgumentTrue(boolean aBoolean, String aMessage) {
        if (!aBoolean) {
            throw new IllegalArgumentException(aMessage);
        }
    }

    protected void assertStateFalse(boolean aBoolean, String aMessage) {
        if (aBoolean) {
            throw new IllegalStateException(aMessage);
        }
    }

    protected void assertStateTrue(boolean aBoolean, String aMessage) {
        if (!aBoolean) {
            throw new IllegalStateException(aMessage);
        }
    }
}
package com.parkingexchange.parking.domain.model;

import com.parkingexchange.common.domain.model.BaseEntity;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDateTime;
import java.util.Objects;
import java.util.UUID;


/**
 * Aggregate Root Entity representing the availability of a parking spot.
 */
@Entity
public class Availability extends BaseEntity {

    @EmbeddedId
    private AvailabilityId availabilityId;

    @NotNull
    @Column(name = "leaving_user_id", nullable = false)
    private UUID leavingUserId;

    @NotNull
    @Valid
    @Embedded
    private Location location;

    @NotNull
    @Future(message = "Estimated leaving time must be in the future")
    @Column(name = "estimated_leaving_time", nullable = false)
    private LocalDateTime estimatedLeavingTime;

    @NotNull
    @Enumerated(EnumType.STRING)
    @Column(name = "status", nullable = false)
    private AvailabilityStatus status;

    @Column(name = "reserved_by_user_id")
    private UUID reservedByUserId;


    protected Availability() {
        // Required by JPA
    }

    private Availability(AvailabilityId availabilityId,
                         UUID leavingUserId,
                         Location location,
                         LocalDateTime estimatedLeavingTime) {
        this.availabilityId = Objects.requireNonNull(availabilityId, "AvailabilityId cannot be null");
        setLeavingUserId(leavingUserId);
        setLocation(location);
        setEstimatedLeavingTime(estimatedLeavingTime);
        this.status = AvailabilityStatus.AVAILABLE;
    }

    // Static factory method for creation
    public static Availability create(UUID leavingUserId, Location location, LocalDateTime estimatedLeavingTime) {
        AvailabilityId availabilityId = AvailabilityId.generate();
        return new Availability(availabilityId, leavingUserId, location, estimatedLeavingTime);
    }

    // Business methods

    /**
     * Reserves this availability for a given arriving user.
     *
     * @param arrivingUserId The ID of the arriving user.
     */
    public void reserve(UUID arrivingUserId) {
        this.assertArgumentTrue(this.getStatus().equals(AvailabilityStatus.AVAILABLE), "Only available spots can be reserved");
        this.reservedByUserId = Objects.requireNonNull(arrivingUserId, "ArrivingUserId cannot be null");
        this.status = AvailabilityStatus.RESERVED;
    }

    /**
     * Confirms that the spot has been taken by the arriving user.
     *
     * @param arrivingUserId The ID of the arriving user.
     */
    public void confirmTaken(UUID arrivingUserId) {
        this.assertArgumentTrue(this.getStatus().equals(AvailabilityStatus.RESERVED), "Only reserved spots can be confirmed as taken");
        this.assertArgumentTrue(this.getReservedByUserId().equals(arrivingUserId), "This spot is not reserved by the provided user");
        this.status = AvailabilityStatus.TAKEN;
    }

    /**
     * Cancels this availability.
     */
    public void cancel(UUID userId) {
        if (!this.leavingUserId.equals(userId) &&
                !Objects.equals(this.reservedByUserId, userId)) {
            throw new IllegalStateException("You do not have permission to cancel this availability");
        }
        if (this.status == AvailabilityStatus.CANCELED) {
            throw new IllegalStateException("Availability is already canceled");
        }
        this.status = AvailabilityStatus.CANCELED;
    }

    // Getters and Setters with validation where applicable

    public AvailabilityId getAvailabilityId() {
        return availabilityId;
    }

    public UUID getLeavingUserId() {
        return leavingUserId;
    }

    public UUID getReservedByUserId(){
        return reservedByUserId;
    }

    private void setLeavingUserId(UUID leavingUserId) {
        this.leavingUserId = Objects.requireNonNull(leavingUserId, "LeavingUserId cannot be null");
    }

    public Location getLocation() {
        return location;
    }

    private void setLocation(Location location) {
        this.location = Objects.requireNonNull(location, "Location cannot be null");
    }

    public LocalDateTime getEstimatedLeavingTime() {
        return estimatedLeavingTime;
    }

    private void setEstimatedLeavingTime(LocalDateTime estimatedLeavingTime) {
        if (estimatedLeavingTime == null) {
            throw new IllegalArgumentException("Estimated leaving time cannot be null");
        }
        if (estimatedLeavingTime.isBefore(LocalDateTime.now())) {
            throw new IllegalArgumentException("Estimated leaving time must be in the future");
        }
        this.estimatedLeavingTime = estimatedLeavingTime;
    }

    public AvailabilityStatus getStatus() {
        return status;
    }

    // equals and hashCode based on 'availabilityId'

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Availability that = (Availability) o;

        return availabilityId.equals(that.availabilityId);
    }

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

    @Override
    public String toString() {
        return "Availability{" +
                "availabilityId=" + availabilityId +
                ", leavingUserId=" + leavingUserId +
                ", location=" + location +
                ", estimatedLeavingTime=" + estimatedLeavingTime +
                ", status=" + status +
                ", reservedByUserId=" + reservedByUserId +
                ", version=" + super.toString() +
                '}';
    }
}Can you cotninue modifying availability in order to use the assertion from the abse class