import java.util.Objects;

/**
 * Un monto de dinero en céntimos. No tiene constructor público: se crea con
 * los static factory methods of, parse y zero.
 */
public final class Money {
    private static final Money ZERO_PEN = new Money(0, "PEN");

    private final long cents;
    private final String currency;

    private Money(long cents, String currency) {
        this.cents = cents;
        this.currency = currency;
    }

    /** Valida y normaliza antes de crear; el cero en soles es siempre el mismo objeto. */
    public static Money of(long cents, String currency) {
        if (cents < 0) {
            throw new IllegalArgumentException("monto negativo: " + cents);
        }
        String code = currency.strip().toUpperCase();
        if (code.length() != 3) {
            throw new IllegalArgumentException("moneda inválida: " + currency);
        }
        if (cents == 0 && code.equals("PEN")) {
            return ZERO_PEN;
        }
        return new Money(cents, code);
    }

    /** Lee un monto en soles escrito como "12.50". */
    public static Money parse(String soles) {
        String[] parts = soles.strip().split("\\.");
        long whole = Long.parseLong(parts[0]);
        long fraction = parts.length > 1 ? Long.parseLong((parts[1] + "0").substring(0, 2)) : 0;
        return of(whole * 100 + fraction, "PEN");
    }

    public static Money zero() {
        return ZERO_PEN;
    }

    public long cents() {
        return cents;
    }

    public String currency() {
        return currency;
    }

    public Money plus(Money other) {
        requireSameCurrency(other);
        return of(cents + other.cents, currency);
    }

    public Money times(long factor) {
        return of(cents * factor, currency);
    }

    public boolean isLessThan(Money other) {
        requireSameCurrency(other);
        return cents < other.cents;
    }

    private void requireSameCurrency(Money other) {
        if (!currency.equals(other.currency)) {
            throw new IllegalArgumentException(currency + " y " + other.currency);
        }
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof Money m && cents == m.cents && currency.equals(m.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(cents, currency);
    }

    @Override
    public String toString() {
        String amount = String.format("%d.%02d", cents / 100, cents % 100);
        return currency.equals("PEN") ? "S/ " + amount : amount + " " + currency;
    }
}
