#!/bin/bash
# function determineDockerState
#
# parameters
#   none
#
# return
#   0 - state of execution is for an installation
#   1 - state of execution is for an upgrade
#
# function will determine if there is already an docker container for the css image, thus an upgrade is needed else install

function determineDockerState
{
    logPoint "Checking Docker install/upgrade state"
    EXECUTION_STATE=${INSTALL}
    DOCKER_LS_RETURN_VAL=$(docker container inspect ${CONTAINER_NAME} 2> /dev/null)
    if [ $? -eq 0 ]
    then
        logPoint "Found an existing container with the name ${CONTAINER_NAME}. Running in UPGRADE mode."
        EXECUTION_STATE=${UPGRADE}
    else
        logPoint "Didn't find an existing container with the name ${CONTAINER_NAME}. Running in INSTALL mode."
    fi

    return ${EXECUTION_STATE}
}

# function getCSSVersionDocker
#
# parameters
#   CONTAINER_NAME - the name of the container that will be checked.
#
# return
# none
#
# function will get the version of the image for the container with the given name. Returns latest if no tag is provided

function getCSSVersionDocker
{
    IMAGE=$(docker container inspect ${CONTAINER_NAME} --format '{{index .Config.Image }}')
    IMAGE_NAME=$(echo ${IMAGE} | cut -f1 -d ":")
    TAG=$(echo ${IMAGE} | cut -f2 -d ":")
    if stringEquals ${IMAGE} ${IMAGE_NAME} || [ "${TAG,,}" = latest ]
    then
        #if the image version wasn't specified (used latest tag at the time) then find the sha of the image, which is unique for each fd version.
        TAG=$(docker container inspect ${CONTAINER_NAME} | grep Image | grep sha | cut -d: -f3 | cut -d\" -f1)
    fi
    CSS_VERSION=${TAG}

}

# function isDockerInstalled
#
# parameters
#   none
#
# return
#   0 - docker is not installed or not on the PATH
#   1 - docker is installed and on the PATH
#
# function will determine if docker is installed and on the PATH

function isDockerInstalled
{
    logPoint "Validating Docker is installed"

    RC=0
    if ! command -v docker &> /dev/null
    then
        generateFatalErrorMessage 131
        exit 1
    fi
    return ${RC}
}

# function isDockerDaemonRunning
#
# parameters
#   none
#
# return
#   0 - docker daemon is not running, or not able to be connected to
#   1 - docker daemon is running and able to be connected to
#
# function will determine if docker is installed and on the PATH

function isDockerDaemonRunning
{
    logPoint "Validating Docker Daemon is running"

    DOCKER_DAEMON_RUNNING=0
    ps -ef | grep dockerd | grep -v grep &> /dev/null
    PS_GREP_DOCKERD_VAL=$?
    variableEmpty ${PS_GREP_DOCKERD_VAL}
    if [ $? -eq 0 ] 
    then
        generateFatalErrorMessage 132
    else
        DOCKER_DAEMON_RUNNING=1
    fi

    return ${DOCKER_DAEMON_RUNNING}
}

# function pullDockerImage
#
# parameters
#   DESIRED_VERSION: The version of the docker container to be used in the pull/install
#
# return
#   0 - docker image pulled successfully
#   any other number - error occurred while pulling docker image
#
# function will determine if docker is installed and on the PATH

function pullDockerImage
{
    if [ "$DESIRED_VERSION" ]
    then
        logPoint "Pulling Docker image for ${DOCKER_REPO}cm/css_tcat:${DESIRED_VERSION}"
        DOCKER_PULL_VAL=$(docker pull ${DOCKER_REPO}cm/css_tcat:"${DESIRED_VERSION}")
    else
        logPoint "Pulling latest Docker image for ${DOCKER_REPO}cm/css_tcat"
        DOCKER_PULL_VAL=$(docker pull ${DOCKER_REPO}cm/css_tcat)
    fi

    return $?
}

# function deleteDockerContainer
#
# parameters
#   CONTAINER_NAME
#
# return
#   0 - docker container deleted
#   1 - docker container delete command encountered an error
#
# function will forcefully delete the docker container with the given container name

function deleteDockerContainer
{
    logPoint "Removing Docker container: ${CONTAINER_NAME}"
    DOCKER_DELETE_VAL=$(docker container rm ${CONTAINER_NAME} -f 2> /dev/null)
    return $?
}

# function startDockerContainer
#
# parameters
#   CONTAINER_NAME
#
# return
#   0 - docker container started
#   1 - docker container start command encountered an error
#
# function will start the docker container with the given container name

function startDockerContainer
{
    logPoint "Starting Docker container: ${CONTAINER_NAME}"
    DOCKER_START_VAL=$(docker start ${CONTAINER_NAME})
    return $?
}

# function stopDockerContainer
#
# parameters
#   CONTAINER_NAME
#
# return
#   0 - docker container stopped
#   1 - docker container stop command encountered an error
#
# function will stop the docker container with the given container name

function stopDockerContainer
{
    logPoint "Stopping Docker container: ${CONTAINER_NAME}"
    DOCKER_STOP_VAL=$(docker stop ${CONTAINER_NAME})
    return $?
}

# function runDockerContainer
#
# parameters
#   DATABASE_URL
#   FD_ADMIN_PWD
#   DB_TYPE
#   CSS_HOME
#   CONTAINER_NAME
#   CSS_PORT
#
# return
#   0 - docker container created and started
#   1 - docker container run command encountered an error
#
# function will execute the docker run command with the given parameters to create and start a docker container with CSS image

function runDockerContainer
{
    isOracleDatabase
    if [ $? -eq 0 ]
    then
        DB_TYPE=oracle
    else
        DB_TYPE=postgres
    fi
    if [ "$DESIRED_VERSION" ]
    then
        logPoint "Running Docker container with version ${DESIRED_VERSION}"
        DOCKER_RUN_VAL=$(docker run -d ${DOCKER_RUN_ADDITIONAL_ARGS} --name ${CONTAINER_NAME} -p ${CSS_PORT}:8081 -e TZ=${CONTAINER_TIMEZONE} -e FLEX_DB_URL=${DATABASE_URL} -e FD_ADMIN_PWD="${CSS_DB_PASSWORD}" -e dbtype=${DB_TYPE} -e MAX_ACTIVE_CONNECTIONS=100 -e MAX_IDLE_CONNECTIONS=100 -e MAX_WAIT_TIME_MILLIS=30000 -v ${CSS_HOME}:/dockervolume/css ${DOCKER_REPO}cm/css_tcat:"${DESIRED_VERSION}")
    else
        logPoint "Running Docker container with latest version"
        DOCKER_RUN_VAL=$(docker run -d ${DOCKER_RUN_ADDITIONAL_ARGS} --name ${CONTAINER_NAME} -p ${CSS_PORT}:8081 -e TZ=${CONTAINER_TIMEZONE} -e FLEX_DB_URL=${DATABASE_URL} -e FD_ADMIN_PWD="${CSS_DB_PASSWORD}" -e dbtype=${DB_TYPE} -e MAX_ACTIVE_CONNECTIONS=100 -e MAX_IDLE_CONNECTIONS=100 -e MAX_WAIT_TIME_MILLIS=30000 -v ${CSS_HOME}:/dockervolume/css ${DOCKER_REPO}cm/css_tcat)
    fi
    return $?
}

# function setDockerCSSOwnershipIds
#
# parameters
#   none
#
# return
#   none
#
# function applies USER_NAMESPACE_OFFSET to the base Docker container uid/gid for chown operations

function setDockerCSSOwnershipIds
{
    DOCKER_CSS_UID=$((DOCKER_CSS_BASE_UID + USER_NAMESPACE_OFFSET))
    DOCKER_CSS_GID=$((DOCKER_CSS_BASE_GID + USER_NAMESPACE_OFFSET))
}

# function validateUserNamespaceOffset
#
# parameters
#   none
#
# return
#   0 - USER_NAMESPACE_OFFSET is valid
#   non-zero - USER_NAMESPACE_OFFSET is not numeric
#
# function validates USER_NAMESPACE_OFFSET when installing with Docker

function validateUserNamespaceOffset
{
    if [ "${DOCKER_INSTALL}" -ne 1 ]
    then
        return 0
    fi

    if variableEmpty ${USER_NAMESPACE_OFFSET}
    then
        USER_NAMESPACE_OFFSET=0
    fi

    if ! [[ "${USER_NAMESPACE_OFFSET}" =~ ^[0-9]+$ ]]
    then
        generateFatalErrorMessage 157 "${CONFIG_FILE}" "USER_NAMESPACE_OFFSET"
        return 157
    fi

    setDockerCSSOwnershipIds
    return 0
}

# function getDockerTargetVersion
#
# parameters
#   none
#
# return
#   sets DOCKER_TARGET_VERSION to the version being installed/upgraded
#
# function resolves the target CSS version for Docker ownership checks

function getDockerTargetVersion
{
    if [ "${DESIRED_VERSION}" = "latest" ]
    then
        if variableNotEmpty "${CSS_VERSION}"
        then
            DOCKER_TARGET_VERSION="${CSS_VERSION}"
        elif variableNotEmpty "${LATEST_VERSION}"
        then
            DOCKER_TARGET_VERSION="${LATEST_VERSION}"
        else
            DOCKER_TARGET_VERSION=""
        fi
    else
        DOCKER_TARGET_VERSION="${DESIRED_VERSION}"
    fi
}

# function isDockerChownVersionRequired
#
# parameters
#   none
#
# return
#   0 - Docker chown to 1283:1283 is required for this version
#   1 - Docker chown is not required for this version
#
# function checks whether the target version is 11.1.0.0 or higher

function isDockerChownVersionRequired
{
    getDockerTargetVersion
    variableEmpty "${DOCKER_TARGET_VERSION}"
    if [ $? -eq 0 ]
    then
        return 1
    fi

    VERSION_FOR_COMPARE=$(echo "${DOCKER_TARGET_VERSION}" | cut -f1-4 -d.)
    CURRENT_VAL=$(calculateNumericalValue "${VERSION_FOR_COMPARE}")
    MIN_VAL=$(calculateNumericalValue "${DOCKER_CHOWN_MIN_VERSION}")
    if [ "${CURRENT_VAL}" -ge "${MIN_VAL}" ]
    then
        return 0
    fi
    return 1
}

# function validateDockerCSSOwnershipPermissions
#
# parameters
#   none
#
# return
#   0 - current user can set ownership for Docker CSS 11.1.0.0+
#   non-zero - current user cannot set ownership
#
# function validates that the installer is running as root when chown is required

function validateDockerCSSOwnershipPermissions
{
    if [ "${DOCKER_INSTALL}" -ne 1 ]
    then
        return 0
    fi

    isDockerChownVersionRequired
    if [ $? -ne 0 ]
    then
        return 0
    fi

    logPoint "Validating installer has permission to set ownership on ${CSS_HOME} to ${DOCKER_CSS_UID}:${DOCKER_CSS_GID}"
    if [ "$(id -u)" -ne 0 ]
    then
        generateFatalErrorMessage 156 "${CSS_HOME}" "${DOCKER_CSS_UID}" "${DOCKER_CSS_GID}"
        return 156
    fi

    return 0
}

# function isDockerCSSHomePathOwnedByContainerUser
#
# parameters
#   $1 - path to check
#
# return
#   0 - path is owned by the Docker container uid/gid
#   1 - path is not owned by the Docker container uid/gid or does not exist
#
# function checks whether a path already has the expected Docker ownership

function isDockerCSSHomePathOwnedByContainerUser
{
    CHECK_PATH=${1}

    if [ ! -e "${CHECK_PATH}" ]
    then
        return 1
    fi

    PATH_OWNER=$(stat -c '%u:%g' "${CHECK_PATH}" 2>/dev/null)
    if [ $? -ne 0 ]
    then
        return 1
    fi

    if [ "${PATH_OWNER}" = "${DOCKER_CSS_UID}:${DOCKER_CSS_GID}" ]
    then
        return 0
    fi

    return 1
}

# function chownForDockerCSSHome
#
# parameters
#   $1 - (optional) path to chown; defaults to CSS_HOME
#   $2 - (optional) force chown even if the path is already owned correctly; use after writes that may add root-owned content
#
# return
#   0 - ownership set or chown not required
#   non-zero - chown failed
#
# function sets ownership on CSS_HOME content for Docker 11.1.0.0+ during install/upgrade only

function chownForDockerCSSHome
{
    CHOWN_PATH=${1:-${CSS_HOME}}
    FORCE_CHOWN=${2:-0}

    if [ "${DOCKER_INSTALL}" -ne 1 ] || [ "${PREREQ_MODE}" -eq 1 ]
    then
        return 0
    fi

    isDockerChownVersionRequired
    if [ $? -ne 0 ]
    then
        return 0
    fi

    # GenAI (Cursor): skip recursive chown when the path is already owned by the container user
    if [ "${FORCE_CHOWN}" -eq 0 ]
    then
        isDockerCSSHomePathOwnedByContainerUser "${CHOWN_PATH}"
        if [ $? -eq 0 ]
        then
            logPoint "Skipping ownership change for ${CHOWN_PATH}; already owned by ${DOCKER_CSS_UID}:${DOCKER_CSS_GID}"
            return 0
        fi
    fi

    logPoint "Setting ownership of ${CHOWN_PATH} to ${DOCKER_CSS_UID}:${DOCKER_CSS_GID}"
    chown -R "${DOCKER_CSS_UID}:${DOCKER_CSS_GID}" "${CHOWN_PATH}" > tmp.$$ 2>&1
    RC=$?
    if [ ${RC} -ne 0 ]
    then
        TEMP_ERROR=$(cat tmp.$$)
        logPoint "Setting ownership of ${CHOWN_PATH} was unsuccessful. Error: ${TEMP_ERROR}"
        generateFatalErrorMessage 156 "${CHOWN_PATH}" "${DOCKER_CSS_UID}" "${DOCKER_CSS_GID}"
    fi
    rm -f tmp.$$ > /dev/null 2>&1
    return ${RC}
}

function dockerPrereq
{
    isDockerDaemonRunning
    isDockerInstalled

    #The docker volume requires these to be constant values.
    if [ "${WORKING_DIRECTORY}" != "${CSS_HOME}/application" ]
    then
        WORKING_DIRECTORY="${CSS_HOME}/application"
        logPoint "Overriding value for WORKING_DIRECTORY to ${WORKING_DIRECTORY}"
    fi
    
    if [ "${ARTIFACT_REPOSITORY_DIRECTORY}" != "${CSS_HOME}/artifacts" ]
    then
        ARTIFACT_REPOSITORY_DIRECTORY=${CSS_HOME}/artifacts
        logPoint "Overriding value for ARTIFACT_REPOSITORY_DIRECTORY to ${ARTIFACT_REPOSITORY_DIRECTORY}"
    fi
    
}