package com.intuit.karate.core;

import com.intuit.karate.PerfHook;
import com.intuit.karate.Runner;
import com.intuit.karate.http.HttpRequest;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.intuit.karate.match.Match;
import com.intuit.karate.match.MatchResult;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author pthomas3
 */
class PerfHookTest {

    static final Logger logger = LoggerFactory.getLogger(PerfHookTest.class);

    static MockServer server;

    @BeforeAll
    static void beforeAll() {
        server = MockServer
                .feature("classpath:com/intuit/karate/core/perf-mock.feature")
                .http(0).build();
        System.setProperty("karate.server.port", server.getPort() + "");
    }

    @BeforeEach
    void beforeEach() {
        eventName = null;
        failed = null;
        vars = null;
    }

    @AfterAll
    static void afterAll() {
        server.stop();
    }

    @Test
    void testPerfHook1() {
        // Run a passing scenario
        List<String> tags = Collections.singletonList("@name=pass");
        String bar = "one";
        Map<String, Object> arg = Collections.singletonMap("bar", bar);
        Runner.callAsync("classpath:com/intuit/karate/core/perf.feature", tags, arg, perfHook);
        assertEquals(eventName, "http://localhost:" + server.getPort() + "/hello?foo=" + bar);
        assertFalse(failed);
        match(vars, "{ bar: '" + bar + "', responseHeaders: { content-type: ['application/json'], content-length: [#string], server: [#string], date: [#string] }, configSource: 'normal', responseStatus: 200, response: { foo: ['" + bar + "'] } }");
    }

    @Test
    void testPerfHook2() {
        // Run a scenario which fails the status check
        List<String> tags = Collections.singletonList("@name=failStatus");
        String bar = "two";
        Map<String, Object> arg = Collections.singletonMap("bar", bar);
        Runner.callAsync("classpath:com/intuit/karate/core/perf.feature", tags, arg, perfHook);
        assertEquals(eventName, "http://localhost:" + server.getPort() + "/hello?foo=" + bar);
        assertTrue(failed);
        match(vars, "{ bar: '" + bar + "', responseHeaders: { content-type: ['application/json'], content-length: [#string], server: [#string], date: [#string] }, configSource: 'normal', responseStatus: 200, response: { foo: ['" + bar + "'] } }");
    }

    @Test
    void testPerfHook3() {
        // Run a scenario which fails the response match
        List<String> tags = Collections.singletonList("@name=failResponse");
        String bar = "three";
        Map<String, Object> arg = Collections.singletonMap("bar", bar);
        Runner.callAsync("classpath:com/intuit/karate/core/perf.feature", tags, arg, perfHook);
        assertEquals(eventName, "http://localhost:" + server.getPort() + "/hello?foo=" + bar);
        assertTrue(failed);
        match(vars, "{ bar: '" + bar + "', responseHeaders: { content-type: ['application/json'], content-length: [#string], server: [#string], date: [#string] }, configSource: 'normal', responseStatus: 200, response: { foo: ['" + bar + "'] } }");
    }

    @Test
    void testPerfHook4() {
        // Run a scenario without passing a required argument
        List<String> tags = Collections.singletonList("@name=pass");
        Map<String, Object> arg = Collections.emptyMap();
        Runner.callAsync("classpath:com/intuit/karate/core/perf.feature", tags, arg, perfHook);
        assertNull(eventName);
        assertTrue(failed);
        match(vars, "{ configSource: 'normal' }");
    }

    @Test
    void testPerfHook5() {
        // Run a scenario which doesn't exist
        List<String> tags = Collections.singletonList("xyz");
        Map<String, Object> arg = Collections.emptyMap();
        Runner.callAsync("classpath:com/intuit/karate/core/perf.feature", tags, arg, perfHook);
        assertNull(eventName);
        assertFalse(failed);
        assertNull(vars);
    }

    private void match(Object actual, Object expected) {
        MatchResult mr = Match.that(actual).isEqualTo(expected);
        assertTrue(mr.pass, mr.message);
    }
    
    String eventName;
    Boolean failed;
    Map<String, Object> vars;

    PerfHook perfHook = new PerfHook() {

        @Override
        public String getPerfEventName(HttpRequest request, ScenarioRuntime sr) {
            return request.getUrl();
        }

        @Override
        public void reportPerfEvent(PerfEvent event) {
            eventName = event.getName();
            logger.debug("perf event: {}", eventName);
        }

        @Override
        public void submit(Runnable runnable) {
            logger.debug("submit called");
            runnable.run();
        }

        @Override
        public void afterFeature(boolean failed, Map<String, Object> vars) {
            logger.debug("afterFeature called");
            PerfHookTest.this.failed = failed;
            PerfHookTest.this.vars = vars;
        }
    };

}

@@ -3962,11 +3962,25 @@
 st(%22
-xyz
+@name=doesntExist
 %22);%0A
@@ -4222,24 +4222,564 @@
 rs);%0A    %7D%0A%0A
+    @Test%0A    void testPerfHook6() %7B%0A        // Run a feature which doesn't exist%0A        List%3CString%3E tags = Collections.emptyList();%0A        Map%3CString, Object%3E arg = Collections.emptyMap();%0A        boolean pass = false;%0A        try %7B%0A            Runner.callAsync(%22classpath:com/intuit/karate/core/doesntExist.feature%22, tags, arg, perfHook);%0A        %7D catch (RuntimeException e) %7B%0A            pass = true;%0A        %7D%0A        assertTrue(pass);%0A        assertNull(eventName);%0A        assertNull(failed);%0A        assertNull(vars);%0A    %7D%0A%0A    
     private 

add test cases for PerfHookTest.java