package me.atam.atam4j;

import me.atam.atam4jdomain.IndividualTestResult;
import me.atam.atam4jdomain.TestsRunResult;
import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class TestRunListener extends RunListener{

    private static final Logger LOGGER = LoggerFactory.getLogger(TestRunListener.class);
    private Map<TestIdentifier, IndividualTestResult> testResults;
    private Map<TestIdentifier, IndividualTestResult> previousTestResults;
    private boolean testsFinished = false;

    @Override
    public void testRunStarted(Description description) throws Exception {
        testResults =  new HashMap<>();
        testsFinished = false;
    }

    @Override
    public void testStarted(Description description) throws Exception {

        testResults.put(
                new TestIdentifier(description),
                new IndividualTestResult(
                        description.getClassName(),
                        description.getMethodName(),
                        getCategoryName(description),
                        true)
        );
    }

    @Override
    public void testFailure(Failure failure) throws Exception {
        IndividualTestResult individualTestResult = testResults.get(new TestIdentifier(failure.getDescription()));
        individualTestResult.setPassed(false);
    }

    public TestsRunResult getTestsRunResult() {
        if (!testsFinished) {
            return new TestsRunResult(TestsRunResult.Status.TOO_EARLY);
        }
        return new TestsRunResult(testResults.values());
    }

    @Override
    public void testRunFinished(Result result) throws Exception {
        this.testsFinished = true;
    }

    private String getCategoryName(Description description) {

        String category = "default";

        try {
            Class testClass = Class.forName(description.getClassName());
            Method testMethod = testClass.getMethod(description.getMethodName());
            if (testMethod.isAnnotationPresent(MonitorCategory.class)) {
                MonitorCategory monitorCategory = testMethod.getAnnotation(MonitorCategory.class);
                category = monitorCategory.name();
            }
        } catch (ClassNotFoundException | NoSuchMethodException e) {
            LOGGER.warn("Test class or method not found", e);
        }

        return category;
    }

    private static class TestIdentifier{
        private Class testClass;
        private String testName;

        public TestIdentifier(Description description) {
            this.testClass = description.getTestClass();
            this.testName = description.getMethodName();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TestIdentifier that = (TestIdentifier) o;
            return Objects.equals(testClass, that.testClass) &&
                    Objects.equals(testName, that.testName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(testClass, testName);
        }
    }
}

@@ -637,17 +637,23 @@
 Result%3E 
-t
+latestT
 estResul
@@ -864,33 +864,88 @@
 ption %7B%0A        
-t
+previousTestResults = latestTestResults;%0A        latestT
 estResults =  ne
@@ -1092,17 +1092,23 @@
         
-t
+latestT
 estResul
@@ -1543,17 +1543,23 @@
 esult = 
-t
+latestT
 estResul
@@ -1710,24 +1710,25 @@
 nResult() %7B%0A
+%0A
         if (
@@ -1727,17 +1727,16 @@
     if (
-!
 testsFin
@@ -1754,32 +1754,152 @@
           return
+ new TestsRunResult(latestTestResults.values());%0A        %7D%0A%0A        return previousTestResults == null%0A                ?
  new TestsRunRes
@@ -1934,17 +1934,16 @@
 O_EARLY)
-;
 %0A       
@@ -1943,26 +1943,24 @@
         
-%7D%0A
         
 return n
@@ -1943,38 +1943,33 @@
                 
-return
+:
  new TestsRunRes
@@ -1972,17 +1972,25 @@
 nResult(
-t
+previousT
 estResul
@@ -1999,24 +1999,25 @@
 .values());%0A
+%0A
     %7D%0A%0A    @

Return the last complete result set