package jinsist.expectations;

import jinsist.matchers.Arguments;
import jinsist.report.FormattedReport;
import jinsist.report.ReportEvent;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import static jinsist.report.NothingEvent.nothing;

public class ReportExpectations implements Expectations {
    private Expectations expectations;
    private List<ReportEvent> log = new ArrayList<>();
    private List<ReportEvent> expected = new ArrayList<>();
    private ReportEvent unexpectedEvent = nothing();

    public ReportExpectations(Expectations expectations) {
        this.expectations = expectations;
    }

    @Override
    public <ReturnType, MockType> void recordStub(
            Class<MockType> classToMock, MockType instance, Method method, Arguments arguments, ReturnType result
    ) {
        expected.add(new ExpectationEvent<>(classToMock, method, arguments));
        expectations.recordStub(classToMock, instance, method, arguments, result);
    }

    @Override
    public <MockType> Object execute(
            Class<MockType> classToMock, MockType instance, Method method, Object[] arguments
    ) {

        ExecuteEvent<MockType> executeEvent = new ExecuteEvent<>(classToMock, method, arguments);
        try {
            Object result = expectations.execute(classToMock, instance, method, arguments);
            expected.remove(0);
            log.add(executeEvent);
            return result;
        } catch (UnexpectedInvocation e) {
            unexpectedEvent = executeEvent;
            FormattedReport report = prepareReport();
            throw new UnexpectedInvocation(report.format(), e);
        }
    }

    @Override
    public void verify() {
        try {
            expectations.verify();
        } catch (UnmetExpectations e) {
            FormattedReport report = prepareReport();
            throw new UnmetExpectations(report.format(), e);
        }
    }

    private FormattedReport prepareReport() {
        ReportEvent firstOrNothing = expected.stream().findFirst().orElse(nothing());
        return new FormattedReport(
                firstOrNothing,
                unexpectedEvent,
                log,
                expected
        );
    }
}

@@ -1375,47 +1375,17 @@
     
-expected.remove(0);%0A            log.add
+updateLog
 (exe
@@ -1470,32 +1470,370 @@
 ) %7B%0A            
+return throwUnexpectedInvocation(executeEvent, e);%0A        %7D%0A    %7D%0A%0A    private %3CMockType%3E void updateLog(ExecuteEvent%3CMockType%3E executeEvent) %7B%0A        expected.remove(0);%0A        log.add(executeEvent);%0A    %7D%0A%0A    private %3CMockType%3E Object throwUnexpectedInvocation(ExecuteEvent%3CMockType%3E executeEvent, UnexpectedInvocation e) %7B%0A        
 unexpectedEvent 
@@ -1848,20 +1848,16 @@
 eEvent;%0A
-    
         
@@ -1890,36 +1890,32 @@
 repareReport();%0A
-    
         throw ne
@@ -1954,34 +1954,24 @@
 rmat(), e);%0A
-        %7D%0A
     %7D%0A%0A    @

Refactor to smaller methods