package com.sonymusic

import com.lesfurets.jenkins.unit.BasePipelineTest
import org.junit.jupiter.api.function.Executable

import static org.hamcrest.MatcherAssert.assertThat
import static org.hamcrest.Matchers.containsString
import static org.hamcrest.Matchers.hasEntry
import static org.junit.jupiter.api.Assertions.*

abstract class BaseGlobalVarTest extends BasePipelineTest {

    List getMethodCalls(String methodName){
        return helper.callStack.findAll { call ->
            call.methodName == methodName
        }
    }

    String getMethodCallArgumentsAsString(String methodName, Integer callIndex = 0){
        def calls = getMethodCalls(methodName)

        return calls[callIndex].argsToString()
    }

    Map getMethodCallArguments(String methodName, Integer callIndex = 0){
        def calls = getMethodCalls(methodName)

        return calls[callIndex].args[0]
    }

    Object getMethodCallArgument(String methodName, String fieldName, Integer callIndex = 0){
        return getMethodCallArguments(methodName, callIndex)[fieldName]
    }

    void assertMethodCalledTimes(String methodName, Integer numCalls){
        def calls = getMethodCalls(methodName)

        assertEquals(numCalls, calls.size(), "Expected method \"${methodName}\" to be called ${numCalls} times.")
    }

    void assertMethodCalledNthWith(String methodName, Integer callIndex, List<String> stringMatches){
        def args = getMethodCallArgumentsAsString(methodName, callIndex)

        assertAll("Expected method \"${methodName}\" to be called at the ${callIndex}th position with matching arguments.",
            *stringMatches.collect{ match -> { -> assertThat(args, containsString(match)) } as Executable }
        )
    }

    void assertMethodCalledNthWith(String methodName, Integer callIndex, Map args){
        def callArgs = getMethodCallArguments(methodName, callIndex)

        println(callArgs.getClass().getSimpleName())

        assertAll("Expected method \"${methodName}\" to be called at the ${callIndex}th position with matching arguments.",
            *args.collect { arg ->
                { -> assertThat(callArgs, hasEntry(arg.key, arg.value)) } as Executable
            }
        )
    }

    void assertMethodCalledNthWith(String methodName, Integer callIndex, String stringMatch) {
        assertMethodCalledNthWith(methodName, callIndex, [stringMatch])
    }

    void assertMethodCalledWith(String methodName, Map args) {
        assertMethodCalledNthWith(methodName, 0, args)
    }

    void assertMethodCalledWith(String methodName, List<String> stringMatches){
        assertMethodCalledNthWith(methodName, 0, stringMatches)
    }

    void assertMethodCalledWith(String methodName, String stringMatch) {
        assertMethodCalledWith(methodName, [stringMatch])
    }

    void assertMethodCalledOnceWith(String methodName, List<String> stringMatches){
        assertMethodCalledTimes(methodName, 1)
        assertMethodCalledWith(methodName, stringMatches)
    }

    void assertMethodCalledOnceWith(String methodName, String stringMatch){
        assertMethodCalledOnceWith(methodName, [stringMatch])
    }

    void assertMethodCalledOnceWith(String methodName, Map args){
        assertMethodCalledTimes(methodName, 1)
        assertMethodCalledWith(methodName, args)
    }

    void assertThrowsWithMessage(Class exceptionClass, String expectedMessage, Closure func){
        def exception = assertThrows(exceptionClass, { func() })

        String actualMessage = exception.getMessage();

        assertThat(actualMessage, containsString(expectedMessage));
    }

    void assertMethodNotCalled(String methodName){
        assertMethodCalledTimes(methodName, 0)
    }
}
