<commit_before>package creditcard_eventsourcing.model;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;

import org.requirementsascode.Model;
import org.requirementsascode.ModelRunner;
import org.requirementsascode.StandardEventHandler;

import creditcard_eventsourcing.model.request.RequestToCloseCycle;
import creditcard_eventsourcing.model.request.RequestsRepay;
import creditcard_eventsourcing.model.request.RequestsToAssignLimit;
import creditcard_eventsourcing.model.request.RequestsWithdrawal;

/**
 * Based on code by <NAME>: 
 * https://gitlab.com/pilloPl/eventsourced-credit-cards/blob/4329a0aac283067f1376b3802e13f5a561f18753
 *
 */
public class CreditCard {

    private UUID uuid;
    private BigDecimal initialLimit;
    private BigDecimal usedLimit = BigDecimal.ZERO;
    private int withdrawals;
    private List<DomainEvent> pendingEvents = new ArrayList<>();
    private Model eventHandlingModel;
    private ModelRunner modelRunner;

    public CreditCard(UUID uuid) {
        this.uuid = uuid;
        this.eventHandlingModel = 
        	Model.builder()
        		.when(this::limitNotAssigned).handles(LimitAssigned.class).with(this::limitAssigned)
        		.when(this::notTooManyWithdrawalsInCycle).handles(CardWithdrawn.class).with(this::cardWithdrawn)
        		.handles(CardRepaid.class).with(this::cardRepaid)
        		.handles(CycleClosed.class).with(this::cycleWasClosed)
        	.build();
        this.modelRunner = new ModelRunner();
        modelRunner.handleWith(this::addingPendingEvents);
        modelRunner.run(eventHandlingModel);
    }
    
    private void addingPendingEvents(StandardEventHandler eventHandler) {
	eventHandler.handleEvent();
	DomainEvent domainEvent = (DomainEvent) eventHandler.getEvent();
	pendingEvents.add(domainEvent);
    }

    class AssignsLimit implements Consumer<RequestsToAssignLimit> {
	@Override
	public void accept(RequestsToAssignLimit request) {
	    BigDecimal amount = request.getAmount();
	    handle(new LimitAssigned(uuid, amount, Instant.now()));
	}
    }

    class Withdraws implements Consumer<RequestsWithdrawal> {
	@Override
	public void accept(RequestsWithdrawal request) {
	    BigDecimal amount = request.getAmount();
	    if (notEnoughMoneyToWithdraw(amount)) {
		throw new IllegalStateException();
	    }
	    handle(new CardWithdrawn(uuid, amount, Instant.now()));
	}
    }

    class Repays implements Consumer<RequestsRepay> {
	@Override
	public void accept(RequestsRepay request) {
	    BigDecimal amount = request.getAmount();
	    handle(new CardRepaid(uuid, amount, Instant.now()));
	}
    }

    class ClosesCycle implements Consumer<RequestToCloseCycle> {
	@Override
	public void accept(RequestToCloseCycle request) {
	    handle(new CycleClosed(uuid, Instant.now()));
	}
    }

    class ThrowsAssignLimitException implements Consumer<RequestsToAssignLimit> {
	public void accept(RequestsToAssignLimit request) {
	    throw new IllegalStateException();
	}
    }

    class ThrowsTooManyWithdrawalsException implements Consumer<ModelRunner> {
	public void accept(ModelRunner request) {
	    throw new IllegalStateException();
	}
    }
    
    private void limitAssigned(LimitAssigned event) {
        this.initialLimit = event.getAmount(); 
    }

    private void cardWithdrawn(CardWithdrawn event) {
        this.usedLimit = usedLimit.add(event.getAmount());
        withdrawals++;
    }

    private void cardRepaid(CardRepaid event) {
        usedLimit = usedLimit.subtract(event.getAmount());
    }

    private void cycleWasClosed(CycleClosed event) {
        withdrawals = 0;
    }
    
    public boolean limitAlreadyAssigned(ModelRunner r) {
        return initialLimit != null;
    }

    private boolean limitNotAssigned(ModelRunner r) {
        return !limitAlreadyAssigned(r);
    }

    public boolean tooManyWithdrawalsInCycle(ModelRunner r) {
        return withdrawals >= 45;
    }
    
    private boolean notTooManyWithdrawalsInCycle(ModelRunner r) {
        return !tooManyWithdrawalsInCycle(r);
    }

    private boolean notEnoughMoneyToWithdraw(BigDecimal amount) {
        return availableLimit().compareTo(amount) < 0;
    }

<commit_msg>Finish refactoring CreditCard example to use model<commit_after>package creditcard_eventsourcing.model;

import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;

import org.requirementsascode.Model;
import org.requirementsascode.ModelRunner;
import org.requirementsascode.StandardEventHandler;

import creditcard_eventsourcing.model.request.RequestToCloseCycle;
import creditcard_eventsourcing.model.request.RequestsRepay;
import creditcard_eventsourcing.model.request.RequestsToAssignLimit;
import creditcard_eventsourcing.model.request.RequestsWithdrawal;

/**
 * Based on code by <NAME>: 
 * https://gitlab.com/pilloPl/eventsourced-credit-cards/blob/4329a0aac283067f1376b3802e13f5a561f18753
 *
 */
public class CreditCard {

    private UUID uuid;
    private BigDecimal initialLimit;
    private BigDecimal usedLimit = BigDecimal.ZERO;
    private int withdrawals;
    private List<DomainEvent> pendingEvents = new ArrayList<>();
    private Model eventHandlingModel;
    private ModelRunner modelRunner;

    public CreditCard(UUID uuid) {
        this.uuid = uuid;
        this.eventHandlingModel = model();
        this.modelRunner = new ModelRunner();
        modelRunner.handleWith(this::addingPendingEvents);
        modelRunner.run(eventHandlingModel);
    }

    private Model model() {
	return Model.builder()
	  .handles(LimitAssigned.class).with(this::limitAssigned)
	  .handles(CardWithdrawn.class).with(this::cardWithdrawn)
	  .handles(CardRepaid.class).with(this::cardRepaid)
	  .handles(CycleClosed.class).with(this::cycleWasClosed)
	.build();
    }
    
    private void addingPendingEvents(StandardEventHandler eventHandler) {
	eventHandler.handleEvent();
	DomainEvent domainEvent = (DomainEvent) eventHandler.getEvent();
	pendingEvents.add(domainEvent);
    }

    class AssignsLimit implements Consumer<RequestsToAssignLimit> {
	@Override
	public void accept(RequestsToAssignLimit request) {
	    BigDecimal amount = request.getAmount();
	    handle(new LimitAssigned(uuid, amount, Instant.now()));
	}
    }

    class Withdraws implements Consumer<RequestsWithdrawal> {
	@Override
	public void accept(RequestsWithdrawal request) {
	    BigDecimal amount = request.getAmount();
	    if (notEnoughMoneyToWithdraw(amount)) {
		throw new IllegalStateException();
	    }
	    handle(new CardWithdrawn(uuid, amount, Instant.now()));
	}
    }

    class Repays implements Consumer<RequestsRepay> {
	@Override
	public void accept(RequestsRepay request) {
	    BigDecimal amount = request.getAmount();
	    handle(new CardRepaid(uuid, amount, Instant.now()));
	}
    }

    class ClosesCycle implements Consumer<RequestToCloseCycle> {
	@Override
	public void accept(RequestToCloseCycle request) {
	    handle(new CycleClosed(uuid, Instant.now()));
	}
    }

    class ThrowsAssignLimitException implements Consumer<RequestsToAssignLimit> {
	public void accept(RequestsToAssignLimit request) {
	    throw new IllegalStateException();
	}
    }

    class ThrowsTooManyWithdrawalsException implements Consumer<ModelRunner> {
	public void accept(ModelRunner request) {
	    throw new IllegalStateException();
	}
    }
    
    private void limitAssigned(LimitAssigned event) {
        this.initialLimit = event.getAmount(); 
    }

    private void cardWithdrawn(CardWithdrawn event) {
        this.usedLimit = usedLimit.add(event.getAmount());
        withdrawals++;
    }

    private void cardRepaid(CardRepaid event) {
        usedLimit = usedLimit.subtract(event.getAmount());
    }

    private void cycleWasClosed(CycleClosed event) {
        withdrawals = 0;
    }
    
    public boolean limitAlreadyAssigned(ModelRunner r) {
        return initialLimit != null;
    }

    public boolean tooManyWithdrawalsInCycle(ModelRunner r) {
        return withdrawals >= 45;
    }

    private boolean notEnoughMoneyToWithdraw(BigDecimal amount) {
        return availableLimit().compareTo(amount) < 0;
    }

{'fraction_non_alphanumeric': 0.09132475033088677, 'fraction_numerical': 0.008181927565876549, 'mean_word_length': 4.127698951264652, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7493660', 'n_tokens_mistral': 2495, 'n_tokens_neox': 2375, 'n_words': 465}