#!/bin/bash
#test:disabled

# TODO: This test takes too long to run. It should be split into multiple little tests.

# we may not need this to run as a pre-check-in test for every PR. but only once in a while to ensure nothing's broken.

set -euo pipefail
source $(dirname $0)/../utils.sh

id=""
ROOT=$(dirname $0)/../..

final_cleanup() {
    rm -rf testDir1/ || true
    kubectl delete ns "ns1-$id" "ns2-$id" &
}

trap final_cleanup EXIT

cleanup() {
    echo "previous response" $?
    [[ -n "${1+x}"  && -n "${2+x}" ]]; fission env delete --name $1 --envns $2 || true
    [[ -n "${3+x}"  && -n "${4+x}" ]]; fission fn delete --name $3 --fns $4 || true
    [[ -n "${5+x}"  && -n "${6+x}" ]]; fission pkg delete --name $5 --pkgns $6 || true
    [[ -n "${7+x}"  && -n "${8+x}" ]]; fission route delete --name $7 --triggerns $8 || true
}

create_python_source_code() {
    mkdir testDir1
    printf 'def main():\n    return "Hello, world!"' > testDir1/hello.py
}

verify_pod_ns() {
    kubectl get deployment -n $2 | grep $1
}

verify_function_pod_ns() {
    function_label=$1
    env_ns=$2

    kubectl get pods -n $env_ns -L functionName| grep $function_label
}


verify_obj_in_ns() {
    obj_kind=$1
    obj_name=$2
    obj_ns=$3

    kubectl get $obj_kind $obj_name -n $obj_ns
}

get_pkg_name_from_func() {
    pkg=`kubectl get function $1 -n $2 -o jsonpath='{.spec.package.packageref.name}'`
    echo "$pkg"
}

# since we havent deleted the funcs created previously in new_deploy_mgr tests,
# just curl on the funcs and verifying http response code should be enough
internal_route_test_2() {
    http_status=`curl -sw "%{http_code}" http://$FISSION_ROUTER/fission-function/func5 -o /tmp/file`
    echo "http_status : $http_status"
    [[ "$http_status" -eq "200" ]]
}

internal_route_test_1() {
    ns="ns2-$id"
    http_status=`curl -sw "%{http_code}" http://$FISSION_ROUTER/fission-function/$ns/func4 -o /tmp/file`
    echo "http_status : $http_status"
    [[ "$http_status" -eq "200" ]]
}

new_deploy_mgr_and_internal_route_test_2() {
    log "Starting new_deploy_mgr_and_internal_route_test_1 with env in default ns"
    fission env create --name python --image ghcr.io/fission/python-env
    fission fn create --name func5 --env python --code testDir1/hello.py --minscale 1 --maxscale 4 --executortype newdeploy
    sleep 15

    # function is loaded in $FISSION_NAMESPACE because func object was created in default ns
    verify_function_pod_ns func5 "$FUNCTION_NAMESPACE" || (log "func func5 not specialized in $FUNCTION_NAMESPACE" &&
    cleanup python "default" func5 "default" "" "" "" "" && exit 1)
    internal_route_test_2 || ( log "internal route test for http://$FISSION_ROUTER/fission-function/func5 returned http_status: $http_status" &&
    cleanup python "default" func5 "default" "" "" "" "" && exit 1)

    cleanup python "default" func5 "default" "" "" "" ""
}

new_deploy_mgr_and_internal_route_test_1() {
    log "Starting new_deploy_mgr_and_internal_route_test_1 with env and fn in different ns"
    fission env create --name python --image ghcr.io/fission/python-env --envns "ns1-$id"
    fission fn create --name func4 --fns "ns2-$id" --env python --envns "ns1-$id" --code testDir1/hello.py --minscale 1 --maxscale 4 --executortype newdeploy
    sleep 15

    # note that this test is diff from pool_mgr test because here function is loaded in func ns and not in env ns
    verify_function_pod_ns func4 "ns2-$id" || (log "func func4 not specialized in ns2-$id" &&
    cleanup python "ns1-$id" func4 "ns2-$id" "" "" "" "" && exit 1)
    internal_route_test_1 || (log "internal route test for http://$FISSION_ROUTER/fission-function/ns2-$id/func4 returned http_status: $http_status" &&
    cleanup python "ns1-$id" func4 "ns2-$id" "" "" "" "" && exit 1)

    cleanup python "ns1-$id" func4 "ns2-$id" "" "" "" ""
}

builder_mgr_test_2() {
    log "Starting builder_mgr_test_2 with env in default ns"
    fission env create --name python-builder-env --builder fission/python-builder --image ghcr.io/fission/python-env
    sleep 180

    # verify the env builder pod came up in fission-builder and env runtime pod came up in fission-function ns
    verify_pod_ns python-builder-env fission-builder || (log "python-builder-env builder env not found in fission-builder ns" &&
    cleanup python-builder-env "default" "" "" "" "" "" "" && exit 1)
    verify_pod_ns python-builder-env $FUNCTION_NAMESPACE || (log "python-builder-env runtime env not found in fission-function ns" &&
    cleanup python-builder-env "default" "" "" "" "" "" "" && exit 1)

    zip -jr src-pkg.zip $ROOT/examples/python/sourcepkg/
    pkg=$(fission package create --src src-pkg.zip --env python-builder-env --buildcmd "./build.sh" --pkgns "ns2-$id"| cut -f2 -d' '| tr -d \')
    sleep 60
    fission fn create --name func4 --fns "ns2-$id" --pkg $pkg --entrypoint "user.main"
    ht=$(fission route create --function func4 --fns "ns2-$id" --url /func4 --method GET | cut -f2 -d' '| tr -d \')

    # get the function loaded into a pod
    sleep 10
    response=$(curl http://$FISSION_ROUTER/func4)
    echo $response
    echo $response | grep -i "a: 1 b: {c: 3, d: 4}" || (log "response a: 1 b: {c: 3, d: 4} not received" &&
    cleanup python-builder-env "default" func4 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id"  && exit 1)


    # verify the function specialized pod is in fission-function. This also verifies builder ran successfully and in fission-builder
    verify_function_pod_ns func4 $FUNCTION_NAMESPACE  || (log "func func4 not specialized in $FUNCTION_NAMESPACE " &&
    cleanup python-builder-env "default" func4 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id"  && exit 1)

    cleanup python-builder-env "default" func4 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id"
}

builder_mgr_test_1() {
    log "Starting builder_mgr_test_1 with env and fn in different ns"
    fission env create --name python-builder-env --envns "ns1-$id" --builder fission/python-builder --image ghcr.io/fission/python-env
    # we need to wait sufficiently for env pods to be up
    sleep 180

    zip -jr src-pkg.zip $ROOT/examples/python/sourcepkg/
    pkg=$(fission package create --src src-pkg.zip --env python-builder-env --envns "ns1-$id" --buildcmd "./build.sh" --pkgns "ns2-$id"| cut -f2 -d' '| tr -d \')
    sleep 60
    fission fn create --name func3 --fns "ns2-$id" --pkg $pkg --entrypoint "user.main"
    ht=$(fission route create --function func3 --fns "ns2-$id" --url /func3 --method GET | cut -f2 -d' '| tr -d \')

    # get the function loaded into a pod
    sleep 10
    response=$(curl http://$FISSION_ROUTER/func3)
    echo "response : $response"
    echo $response | grep -i "a: 1 b: {c: 3, d: 4}" || (log "response a: 1 b: {c: 3, d: 4} not received" &&
    cleanup python-builder-env "ns1-$id" func3 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id"  && exit 1)

    # verify the function specialized pod is in ns1-$id. This also verifies builder ran successfully and in ns1-$id
    verify_function_pod_ns func3 "ns1-$id" || (log "func func3 not specialized in ns1-$id" &&
    cleanup python-builder-env "ns1-$id" func3 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id" && exit 1)

    cleanup python-builder-env "ns1-$id" func3 "ns2-$id" "$pkg" "ns2-$id" $ht "ns2-$id"
}

pool_mgr_test_2() {
    log "Starting pool_mgr_test_2 with env in default ns"
    fission env delete --name python || true
    fission env create --name python --image ghcr.io/fission/python-env
    fission fn create --name func2 --fns "ns2-$id" --env python --code testDir1/hello.py
    ht=$(fission route create --function func2 --fns "ns2-$id" --url /func2 | cut -f2 -d' '| tr -d \')

    # verify that env object is created in default ns when envns option is absent with fission env create command
    verify_obj_in_ns environment python default|| (log "env python not found in default ns" &&
    cleanup python "default" func2 "ns2-$id" "" "" $ht "ns2-$id" && exit 1)

    sleep 3
    # get the function loaded into a pod
    response=$(curl http://$FISSION_ROUTER/func2)
    echo $response | grep "Hello" || (log "response Hello not received" &&
    cleanup python "default" func2 "ns2-$id" "" "" $ht "ns2-$id" && exit 1)

    # note that the env pod is created in the $FUNCTION_NAMESPACE ns though the env object is created in default ns
    # so even if the function is created in a different ns, they will be loaded in the $FUNCTION_NAMESPACE.
    verify_function_pod_ns func2 $FUNCTION_NAMESPACE || (log "func func2 not specialized in $FUNCTION_NAMESPACE" &&
    cleanup python "default" func2 "ns2-$id" "" "" $ht "ns2-$id" && exit 1)

    cleanup python "default" func2 "ns2-$id" "" "" $ht "ns2-$id"
}

pool_mgr_test_1() {
    log "Starting pool_mgr_test_1 with env and fn in different ns"
    fission env create --name python --image ghcr.io/fission/python-env --envns "ns1-$id"
    fission fn create --name func1 --fns "ns2-$id" --env python --envns "ns1-$id" --code testDir1/hello.py
    ht=$(fission route create --function func1 --fns "ns2-$id" --url /func1 | cut -f2 -d' '| tr -d \')

    # verify that fission objects are created in the expected namespaces
    verify_obj_in_ns environment python "ns1-$id" || (log "env python not found in ns1-$id" &&
    cleanup python "ns1-$id" && exit 1)
    verify_obj_in_ns "function" func1 "ns2-$id" || (log "function func1 not found in ns2-$id" &&
    cleanup python "ns1-$id" func1 "ns2-$id" && exit 1)
    pkg=$( get_pkg_name_from_func func1 "ns2-$id" )
    verify_obj_in_ns package $pkg "ns2-$id" || (log "package $pkg not found in ns2-$id" &&
    cleanup python "ns1-$id" func1 "ns2-$id" $pkg "ns2-$id" && exit 1)
    verify_obj_in_ns httptrigger $ht "ns2-$id" || (log "http trigger $ht not found in ns2-$id" &&
    cleanup python "ns1-$id" func1 "ns2-$id" $pkg "ns2-$id" $ht "ns2-$id" && exit 1)

    sleep 3
    # get the function loaded into a pod
    response=$(curl http://$FISSION_ROUTER/func1)
    echo $response | grep "Hello" || (log "response Hello not received" &&
    cleanup python "ns1-$id" func1 "ns2-$id" $pkg "ns2-$id" $ht "ns2-$id" && exit 1)

    # note that the env pod is created in the ns that env is created
    # so even if the function is created in a different ns, they will be loaded in the env pods ns.
    # this behavior is for poolmgr so that functions can utilize the resources better.
    verify_function_pod_ns func1 "ns1-$id" || (log "func func1 not specialized in ns1-$id" &&
    cleanup python "ns1-$id" func1 "ns2-$id" $pkg "ns2-$id" $ht "ns2-$id" && exit 1)

    cleanup python "ns1-$id" func1 "ns2-$id" $pkg "ns2-$id" $ht "ns2-$id"
}


main() {
    # extract the test-id generated for this CI test run, so that they can be suffixed to namespaces created as part of
    # this test and namespaces won't clash when fission CI tests are run in parallel in the future.
    id=`echo $FISSION_NAMESPACE| cut -d"-" -f2`

    echo "test_id : $id"

    # create source code
    create_python_source_code

    # pool mgr tests
    # 1. env ns1, func ns2 with code, route and verify specialized pod in ns1, also verify pkg in ns2
    pool_mgr_test_1

    # 3. env default, func with code in ns2, route and verify specialized pod in $FUNCTION_NAMESPACE.
    # this test is to verify backward compatibility
    pool_mgr_test_2

    # builder mgr tests
    # 1. env with builder image and runtime image in ns1, src pkg and func in ns2, route and verify specialized pod in ns1
    builder_mgr_test_1

    # 2. env with builder image and runtime image in default, src pkg and func in ns2,
    #    route and verify specialized pod in fission-builder. this test is to verify backward compatibility
    builder_mgr_test_2

    # new deploy mgr tests
    # 1. env ns1, func ns2 with code, route and verify specialized pod in ns2,
    new_deploy_mgr_and_internal_route_test_1

    # 2. env default, func ns2 with code, route and verify specialized pod in fission-function
    new_deploy_mgr_and_internal_route_test_2

    # internal route tests. ( combined this with new deploy mgr tests, so we dont have to recreate envs and funcs again )
    # 1. env ns1, func ns2 with code, curl http://FISSION_ROUTER/fission-function/ns2/func -> should work
    # 2. env in default, func ns2 with code, curl http://FISSION_ROUTER/fission-function/func -> should work


    # TODO : add following tests
    # timer trigger tests.
    # 1. env ns1, func ns2 with code, tt ( with a one time cron string for executing imm'ly), verify function is executed
    # this also indirectly tests internal route established at http://FISSION_ROUTER/ns1/func

    # 2. env in default, func ns2 with code, tt ( with a one time cron string for executing imm'ly), verify function is executed
    # this also indirectly tests internal route established at http://FISSION_ROUTER/func


    # kube watch tests.
    # 1. env ns1, func ns2 with code, watch Trigger ( TBD), verify function is executed
    # this also indirectly tests internal route established at http://FISSION_ROUTER/ns1/func

    # 2. env in default, func ns2 with code, watch Trigger ( TBD ), verify function is executed
    # this also indirectly tests internal route established at http://FISSION_ROUTER/func


    # msq trigger tests.
    # integrate after mqtrigger tests are checked into master.

    final_cleanup
}

main
