// Path: expectations/src/main/java/jinsist/expectations/Expectations.java
// public interface Expectations {
//     <ReturnType, MockType> void recordStub(
//             Class<MockType> classToMock,
//             MockType instance,
//             Method method,
//             Arguments arguments,
//             ReturnType result
//     );
// 
//     <MockType> Object execute(
//             Class<MockType> classToMock,
//             MockType instance,
//             Method method,
//             Object[] arguments
//     );
// 
//     void verify();
// }
// 
// Path: mock/src/main/java/jinsist/mock/delegators/MockExecutor.java
// public class MockExecutor<MockType> implements Delegator<MockType> {
//     private Expectations expectations;
//     private Class<MockType> mockType;
// 
//     public MockExecutor(Expectations expectations, Class<MockType> mockType) {
//         this.expectations = expectations;
//         this.mockType = mockType;
//     }
// 
//     @Override
//     public Object handle(MockType instance, Method method, Object[] arguments) {
//         return expectations.execute(mockType, instance, method, arguments);
//     }
// }
// 
// Path: mock/src/main/java/jinsist/mock/delegators/SetupRecorder.java
// public class SetupRecorder<ReturnType, MockType> implements Delegator<MockType> {
// 
//     private MockType instance;
//     private ReturnType result;
//     private SetupResult setupResult;
//     private Expectations expectations;
//     private Class<MockType> mockType;
// 
//     public SetupRecorder(
//             Expectations expectations,
//             Class<MockType> mockType,
//             MockType instance,
//             ReturnType result,
//             SetupResult setupResult
//     ) {
//         this.expectations = expectations;
//         this.mockType = mockType;
//         this.instance = instance;
//         this.result = result;
//         this.setupResult = setupResult;
//     }
// 
//     @Override
//     public ReturnType handle(MockType setupInstance, Method method, Object[] args) {
//         verifyReturnTypeNeedsToBeStubbed(result, method);
// 
//         Arguments arguments = makeArguments(args);
// 
//         expectations.recordStub(mockType, instance, method, arguments, result);
//         setupResult.setSuccess();
//         return result;
//     }
// 
//     private Arguments makeArguments(Object[] args) {
//         return new Arguments(
//                 stream(args).map(EqualsMatcher::new).collect(Collectors.toList())
//         );
//     }
// 
//     private void verifyReturnTypeNeedsToBeStubbed(ReturnType result, Method method) {
//         Class<?> returnType = method.getReturnType();
//         if (!returnType.equals(Void.TYPE) && returnType.isPrimitive() && result == null) {
//             throw new UnableToStubPrimitiveReturnType();
//         }
//     }
// }
// 
// Path: proxy/src/main/java/jinsist/proxy/Delegator.java
// public interface Delegator<T> {
//     Object handle(T instance, Method method, Object[] arguments);
// }
// 
// Path: proxy/src/main/java/jinsist/proxy/Proxy.java
// public class Proxy<T> {
//     private Class<T> type;
// 
//     private Class<? extends T> makeProxyFor(Class<T> classToMock, Delegator<T> delegator) {
//         ProxyDelegate<T> delegate = new ProxyDelegate<>(delegator);
//         return new ByteBuddy()
//                 .subclass(classToMock)
//                 .method(ElementMatchers.any())
//                 .intercept(MethodDelegation.to(delegate))
//                 .make()
//                 .load(classToMock.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
//                 .getLoaded();
// 
//     }
// 
//     public Proxy(Class<T> type) {
//         this.type = type;
//     }
// 
//     public T instance(Delegator<T> delegator) {
//         try {
//             T instance = makeProxyFor(type, delegator).newInstance();
//             return instance;
//         } catch (InstantiationException | IllegalAccessException | IllegalAccessError e) {
//             throw new ProxyInstanceCreationFailed(type, e);
//         }
//     }
// 
// }


import jinsist.expectations.Expectations;
import jinsist.mock.delegators.MockExecutor;
import jinsist.mock.delegators.SetupRecorder;
import jinsist.proxy.Delegator;
import jinsist.proxy.Proxy;
package jinsist.mock;


public class Mock<MockType> {

    private Class<MockType> mockClass;

    public MockType getInstance() {
        return instance;
    }

    private MockType instance;
    private Expectations expectations;

    public Mock(Class<MockType> mockClass, Expectations expectations) {
        MockType instance = new Proxy<>(mockClass).instance(new MockExecutor<>(expectations, mockClass));
        this.mockClass = mockClass;
        this.instance = instance;
        this.expectations = expectations;
    }

    public <ReturnType> Returns<ReturnType, MockType> query(StubCall<ReturnType, MockType> call) {
        return new Returns<>(call, this);
    }

    Voids<MockType> command(ExpectationCall<MockType> call) {
        return new Voids<>(call, this);
    }

    <ReturnType> MockType setupInstanceWithResult(ReturnType result, SetupResult setupResult) {
        Delegator<MockType> recorder = new SetupRecorder<>(expectations, mockClass, instance, result, setupResult);

        return new Proxy<>(mockClass).instance(recorder);
    }

    MockType setupInstance(SetupResult setupResult) {
// Path: expectations/src/main/java/jinsist/expectations/Expectations.java
// public interface Expectations {
//     <ReturnType, MockType> void recordStub(
//             Class<MockType> classToMock,
//             MockType instance,
//             Method method,
//             Arguments arguments,
//             ReturnType result
//     );
// 
//     <MockType> Object execute(
//             Class<MockType> classToMock,
//             MockType instance,
//             Method method,
//             Object[] arguments
//     );
// 
//     void verify();
// }
// 
// Path: mock/src/main/java/jinsist/mock/delegators/MockExecutor.java
// public class MockExecutor<MockType> implements Delegator<MockType> {
//     private Expectations expectations;
//     private Class<MockType> mockType;
// 
//     public MockExecutor(Expectations expectations, Class<MockType> mockType) {
//         this.expectations = expectations;
//         this.mockType = mockType;
//     }
// 
//     @Override
//     public Object handle(MockType instance, Method method, Object[] arguments) {
//         return expectations.execute(mockType, instance, method, arguments);
//     }
// }
// 
// Path: mock/src/main/java/jinsist/mock/delegators/SetupRecorder.java
// public class SetupRecorder<ReturnType, MockType> implements Delegator<MockType> {
// 
//     private MockType instance;
//     private ReturnType result;
//     private SetupResult setupResult;
//     private Expectations expectations;
//     private Class<MockType> mockType;
// 
//     public SetupRecorder(
//             Expectations expectations,
//             Class<MockType> mockType,
//             MockType instance,
//             ReturnType result,
//             SetupResult setupResult
//     ) {
//         this.expectations = expectations;
//         this.mockType = mockType;
//         this.instance = instance;
//         this.result = result;
//         this.setupResult = setupResult;
//     }
// 
//     @Override
//     public ReturnType handle(MockType setupInstance, Method method, Object[] args) {
//         verifyReturnTypeNeedsToBeStubbed(result, method);
// 
//         Arguments arguments = makeArguments(args);
// 
//         expectations.recordStub(mockType, instance, method, arguments, result);
//         setupResult.setSuccess();
//         return result;
//     }
// 
//     private Arguments makeArguments(Object[] args) {
//         return new Arguments(
//                 stream(args).map(EqualsMatcher::new).collect(Collectors.toList())
//         );
//     }
// 
//     private void verifyReturnTypeNeedsToBeStubbed(ReturnType result, Method method) {
//         Class<?> returnType = method.getReturnType();
//         if (!returnType.equals(Void.TYPE) && returnType.isPrimitive() && result == null) {
//             throw new UnableToStubPrimitiveReturnType();
//         }
//     }
// }
// 
// Path: proxy/src/main/java/jinsist/proxy/Delegator.java
// public interface Delegator<T> {
//     Object handle(T instance, Method method, Object[] arguments);
// }
// 
// Path: proxy/src/main/java/jinsist/proxy/Proxy.java
// public class Proxy<T> {
//     private Class<T> type;
// 
//     private Class<? extends T> makeProxyFor(Class<T> classToMock, Delegator<T> delegator) {
//         ProxyDelegate<T> delegate = new ProxyDelegate<>(delegator);
//         return new ByteBuddy()
//                 .subclass(classToMock)
//                 .method(ElementMatchers.any())
//                 .intercept(MethodDelegation.to(delegate))
//                 .make()
//                 .load(classToMock.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
//                 .getLoaded();
// 
//     }
// 
//     public Proxy(Class<T> type) {
//         this.type = type;
//     }
// 
//     public T instance(Delegator<T> delegator) {
//         try {
//             T instance = makeProxyFor(type, delegator).newInstance();
//             return instance;
//         } catch (InstantiationException | IllegalAccessException | IllegalAccessError e) {
//             throw new ProxyInstanceCreationFailed(type, e);
//         }
//     }
// 
// }


// Path: mock/src/main/java/jinsist/mock/Mock.java
import jinsist.expectations.Expectations;
import jinsist.mock.delegators.MockExecutor;
import jinsist.mock.delegators.SetupRecorder;
import jinsist.proxy.Delegator;
import jinsist.proxy.Proxy;

package jinsist.mock;


public class Mock<MockType> {

    private Class<MockType> mockClass;

    public MockType getInstance() {
        return instance;
    }

    private MockType instance;
    private Expectations expectations;

    public Mock(Class<MockType> mockClass, Expectations expectations) {
        MockType instance = new Proxy<>(mockClass).instance(new MockExecutor<>(expectations, mockClass));
        this.mockClass = mockClass;
        this.instance = instance;
        this.expectations = expectations;
    }

    public <ReturnType> Returns<ReturnType, MockType> query(StubCall<ReturnType, MockType> call) {
        return new Returns<>(call, this);
    }

    Voids<MockType> command(ExpectationCall<MockType> call) {
        return new Voids<>(call, this);
    }

    <ReturnType> MockType setupInstanceWithResult(ReturnType result, SetupResult setupResult) {
        Delegator<MockType> recorder = new SetupRecorder<>(expectations, mockClass, instance, result, setupResult);

        return new Proxy<>(mockClass).instance(recorder);
    }

    MockType setupInstance(SetupResult setupResult) {
        return setupInstanceWithResult(null, setupResult);