package com.publicuhc.pluginframework.routing;

import com.publicuhc.pluginframework.routing.exception.CommandInvocationException;
import com.publicuhc.pluginframework.routing.proxy.MethodProxy;
import com.publicuhc.pluginframework.routing.proxy.ReflectionMethodProxy;
import junit.framework.AssertionFailedError;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.libs.joptsimple.OptionParser;
import org.bukkit.craftbukkit.libs.joptsimple.OptionSet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;

import java.lang.reflect.Method;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;

@RunWith(PowerMockRunner.class)
public class DefaultCommandRouteTest
{
    private OptionParser parser;
    private TestClass testObject;

    @Before
    public void onStartup() throws NoSuchMethodException
    {
        testObject = mock(TestClass.class);
        parser = mock(OptionParser.class);
    }

    @Test
    public void testValidInvocation() throws Throwable
    {
        Method method = TestClass.class.getMethod("testMethod", Command.class, CommandSender.class, OptionSet.class);
        MethodProxy proxy = spy(new ReflectionMethodProxy(testObject, method));
        parser = mock(OptionParser.class);
        DefaultCommandRoute route = new DefaultCommandRoute("test", proxy, parser);

        Command command = mock(Command.class);
        CommandSender sender = mock(CommandSender.class);
        String[] args = new String[]{"1", "2"};

        route.run(command, sender, args);

        verify(parser, times(1)).parse(args);
        verify(proxy, times(1)).invoke(same(command), same(sender), any(OptionSet.class));
        verify(testObject, times(1)).testMethod(same(command), same(sender), any(OptionSet.class));
    }

    @Test
    public void testExceptionInvocation() throws Throwable
    {
        Method method = TestClass.class.getMethod("testExceptionMethod", Command.class, CommandSender.class, OptionSet.class);
        MethodProxy proxy = spy(new ReflectionMethodProxy(testObject, method));
        parser = mock(OptionParser.class);
        DefaultCommandRoute route = new DefaultCommandRoute("test", proxy, parser);

        Command command = mock(Command.class);
        CommandSender sender = mock(CommandSender.class);
        String[] args = new String[]{"1", "2"};

        try {
            route.run(command, sender, args);
            verify(parser, times(1)).parse(args);
            verify(proxy, times(1)).invoke(same(command), same(sender), any(OptionSet.class));
            verify(testObject, times(1)).testExceptionMethod(same(command), same(sender), any(OptionSet.class));
            return;
        } catch(CommandInvocationException ignored) {}
        throw new AssertionFailedError("Expected CommandInvocationException");
    }

    private class TestClass
    {
        public static final String TEST_STRING = "TEST STRING";
        public String testMethod(Command command, CommandSender sender, OptionSet set)
        {
            return TEST_STRING;
        }
        public String testExceptionMethod(Command command, CommandSender sender, OptionSet set)
        {
            throw new IllegalStateException();
        }
    }
}

@@ -669,16 +669,74 @@
 ethod;%0A%0A
+import static org.assertj.core.api.Assertions.assertThat;%0A
 import s
@@ -1244,21 +1244,20 @@
 bject = 
-mock(
+new 
 TestClas
@@ -1257,22 +1257,17 @@
 estClass
-.class
+(
 );%0A     
@@ -1549,32 +1549,43 @@
 ctionMethodProxy
+%3CTestClass%3E
 (testObject, met
@@ -1865,32 +1865,50 @@
 g%5B%5D%7B%221%22, %222%22%7D;%0A%0A
+        try %7B%0A    
         route.ru
@@ -1933,16 +1933,118 @@
  args);%0A
+        %7D catch(CommandInvocationException ex) %7B%0A            throw ex.getWrappedException();%0A        %7D
 %0A       
@@ -2173,24 +2173,25 @@
 lass));%0A
+%0A
         
 verify(t
@@ -2174,38 +2174,42 @@
 ass));%0A%0A        
-verify
+assertThat
 (testObject, tim
@@ -2207,80 +2207,26 @@
 ject
-, times(1)).testMethod(same(command), same(sender), any(OptionSet.class)
+.wasRan()).isTrue(
 );%0A 
@@ -2357,21 +2357,17 @@
 Method(%22
-testE
+e
 xception
@@ -2486,16 +2486,27 @@
 hodProxy
+%3CTestClass%3E
 (testObj
@@ -2854,32 +2854,169 @@
  sender, args);%0A
+            throw new AssertionFailedError(%22Expected CommandInvocationException%22);%0A        %7D catch(CommandInvocationException ignored) %7B%0A
             veri
@@ -3156,22 +3156,26 @@
         
-verify
+assertThat
 (testObj
@@ -3181,350 +3181,124 @@
 ject
-, times(1)).testExceptionMethod(same(command), same(sender), any(OptionSet.class));%0A            return;
+.wasRan()).isFalse();%0A        %7D
 %0A    
+%7D%0A%0A
     
-%7D catch(CommandInvocationException ignored) %7B%7D%0A        throw new AssertionFailedError(%22Expected CommandInvocationException%22);%0A    %7D%0A%0A    private class TestClass%0A    %7B%0A        public static final String TEST_STRING = %22TEST STRING%22;
+public static class TestClass%0A    %7B%0A        private boolean ran = false;%0A
 %0A   
@@ -3305,30 +3305,28 @@
      public 
-String
+void
  testMethod(
@@ -3405,26 +3405,20 @@
     
-return TEST_STRING
+setRan(true)
 ;%0A  
@@ -3444,20 +3444,14 @@
 lic 
-String testE
+void e
 xcep
@@ -3578,20 +3578,184 @@
 ;%0A        %7D%0A
+        public void setRan(boolean ran)%0A        %7B%0A            this.ran = ran;%0A        %7D%0A        public boolean wasRan()%0A        %7B%0A            return ran;%0A        %7D%0A
     %7D%0A%7D%0A

fix the tests as we can't use verify when using reflection