package com.sonymusic

import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test

import static org.hamcrest.MatcherAssert.assertThat
import static org.hamcrest.core.StringContains.containsString
import static org.junit.jupiter.api.Assertions.assertEquals
import static org.junit.jupiter.api.Assertions.assertNull

class CdnInvalidateTest extends BaseGlobalVarTest {
    def cdnInvalidate

    @BeforeEach
    void setUp() {
        super.setUp()
        cdnInvalidate = loadScript('vars/cdnInvalidate.groovy')
        helper.registerAllowedMethod('withAWS', [Map, Closure])
        helper.registerAllowedMethod('withEnv', [Map, Closure])
        helper.registerAllowedMethod('cfInvalidate', [Map])
        helper.registerAllowedMethod('checkout', [List])
        helper.registerAllowedMethod('dir', [String])
        helper.registerAllowedMethod('sh', [String])
    }

    @Test
    void testCallWithInvalidArgs() {
        assertThrowsWithMessage(
            IllegalArgumentException.class, 
            "cdnInvalidate: Missing required parameter: 'appName'",
            { cdnInvalidate.call(env: 'dev') }
        )
        assertThrowsWithMessage(
            IllegalArgumentException.class, 
            "cdnInvalidate: Missing required parameter: 'env'",
            { cdnInvalidate.call(appName: 'test') }
        )
    }

    @Test
    void testCallWithUnknownEnv() {
        assertThrowsWithMessage(
            AssertionError.class, 
            'cdnInvalidate: No distributions defined for the "dev" environment.',
            { cdnInvalidate.call(env: 'dev', appName: 'test') }
        )
    }

    @Test
    void testCallWithEnvQa() {
        def appName = "test-app"

        assertNull(
            cdnInvalidate(appName: appName, env: 'qa')
        )

        assertMethodCalledOnceWith('withAWS', 'region=us-east-1')
        assertMethodCalledTimes('cfInvalidate', 2)
        assertMethodCalledNthWith('cfInvalidate', 0, [
            'distribution=E1TQKM1RJXX2MF',
            'paths=[/test-app/*.html, /test-app/favicon.*, /test-app/main.dml.*, /test-app/dml.*, /test-app/manifest*, /test-app/.well-known/*]',
            'waitForCompletion=false'
        ])
        assertMethodCalledNthWith('cfInvalidate', 1, [
            'distribution=E1V2KPLK0JZWXL',
            'paths=[/test-app/*.html, /test-app/favicon.*, /test-app/main.dml.*, /test-app/dml.*, /test-app/manifest*, /test-app/.well-known/*]',
            'waitForCompletion=false'
        ])

        assertMethodCalledOnceWith('checkout', 'url=git@github.com:theorchard/python-deployment-utils.git')
        assertMethodCalledNthWith('dir', 0, 'cdnInvalidate')
        assertMethodCalledNthWith('dir', 1, 'cloudfront')

        assertMethodCalledOnceWith('withEnv', [
            "APP_NAME=${appName}",
            'AWS_DEFAULT_REGION=us-east-1',
            'ENV=qa'
        ])

        assertMethodCalledOnceWith('sh', 'python3 -u invalidate_caches.py')
    }

    @Test
    void testCallWithEnvProd() {
        def appName = "test-app"

        assertNull(
            cdnInvalidate(appName: appName, env: 'prod')
        )

        assertMethodCalledOnceWith('withAWS', 'region=us-east-1')
        assertMethodCalledTimes('cfInvalidate', 2)
        assertMethodCalledNthWith('cfInvalidate', 0, [
            'distribution=E3U24DC9WT11IH',
            'paths=[/test-app/*.html, /test-app/favicon.*, /test-app/main.dml.*, /test-app/dml.*, /test-app/manifest*, /test-app/.well-known/*]',
            'waitForCompletion=false'
        ])
        assertMethodCalledNthWith('cfInvalidate', 1, [
            'distribution=E2JV6Y8XZK7U5C',
            'paths=[/test-app/*.html, /test-app/favicon.*, /test-app/main.dml.*, /test-app/dml.*, /test-app/manifest*, /test-app/.well-known/*]',
            'waitForCompletion=false'
        ])

        assertMethodCalledOnceWith('checkout', 'url=git@github.com:theorchard/python-deployment-utils.git')
        assertMethodCalledNthWith('dir', 0, 'cdnInvalidate')
        assertMethodCalledNthWith('dir', 1, 'cloudfront')

        assertMethodCalledOnceWith('withEnv', [
            "APP_NAME=${appName}",
            'AWS_DEFAULT_REGION=us-east-1',
            'ENV=prod'
        ])

        assertMethodCalledOnceWith('sh', 'python3 -u invalidate_caches.py')
    }

    @Test
    void testCallWithCustomDistributions() {
        def appName = "test-app"
        def distributions = ["1234"]

        assertNull(cdnInvalidate(
            appName: appName,
            env: 'qa',
            distributions: distributions
        ))

        assertMethodCalledOnceWith('cfInvalidate', [
            'distribution=1234',
            'paths=[/test-app/*.html, /test-app/favicon.*, /test-app/main.dml.*, /test-app/dml.*, /test-app/manifest*, /test-app/.well-known/*]',
            'waitForCompletion=false'
        ])

        assertMethodCalledTimes('checkout', 1)
        assertMethodCalledTimes('dir', 2)
        assertMethodCalledTimes('withEnv', 1)
        assertMethodCalledTimes('sh', 1)
    }

    @Test
    void testCallWithCustomRegion() {
        def region = "us-west-1"

        assertNull(
            cdnInvalidate(appName: 'frontend-test', env: 'qa', awsRegion:  "us-west-1")
        )

        assertMethodCalledOnceWith('withAWS', 'region=us-west-1')
        assertMethodCalledTimes('cfInvalidate', 2)
        assertMethodCalledTimes('checkout', 1)
        assertMethodCalledTimes('dir', 2)
        assertMethodCalledOnceWith('withEnv', [
            "APP_NAME=frontend-test",
            'AWS_DEFAULT_REGION=us-west-1',
            'ENV=qa'
        ])
        assertMethodCalledTimes('sh', 1)
    }

    @Test
    void testCallWithWaitForCompletion() {
        assertNull(cdnInvalidate(
            appName: 'test-app',
            env: 'qa',
            waitForCompletion: true
        ))

        def cfInvalidateSteps = helper.callStack.findAll { call ->
            call.methodName == 'cfInvalidate'
        }

        assertEquals(2, cfInvalidateSteps.size())

        def cfInvalidateArgs1 = cfInvalidateSteps[0].argsToString();
        def cfInvalidateArgs2 = cfInvalidateSteps[1].argsToString();

        assertThat(cfInvalidateArgs1, containsString("waitForCompletion=true"))
        assertThat(cfInvalidateArgs2, containsString("waitForCompletion=true"))
    }
}
