#!/bin/bash

#####################################################################
####### Script instalador Ogclient
####### Autor: Luis Gerardo Romero <lguillen@unizar.es>
#####################################################################

function globalSetup() {
    local current_dir
    current_dir=$(dirname "$0")
    PROGRAMDIR=$(readlink -e "$current_dir")
    PROGRAMNAME=$(basename "$0")
    OPENGNSYS_CLIENT_USER="opengnsys"
    current_dir=$(dirname "$0")
    PROGRAMDIR=$(readlink -e "$current_dir")

    # Ruta del archivo config_ogdhcp.json proporcionado por el usuario
    CONFIG_FILE="$PROGRAMDIR/config_ogdhcp.json"


    # Comprobar si se ha descargado el paquete comprimido (REMOTE=0) o sólo el instalador (REMOTE=1).
    if [ -d "$PROGRAMDIR/../installer" ]; then
        echo "REMOTE=0"
        REMOTE=0
    else
        echo "REMOTE=1"
        REMOTE=1
    fi

    BRANCH=${1:-"main"}

    GIT_REPO="ssh://git@ognproject.evlt.uma.es:21987/opengnsys/ogdhcp.git"

    # Directorios de instalación y destino de OpenGnsys.
    WORKDIR=/tmp/ogdhcp_installer
    INSTALL_TARGET=$(jq -r '.ogDhcp_Dir' "$CONFIG_FILE")
    PATH=$PATH:$INSTALL_TARGET/bin

    if command -v service &>/dev/null; then
        STARTSERVICE="eval service \$service restart"
        STOPSERVICE="eval service \$service stop"
    else
        STARTSERVICE="eval /etc/init.d/\$service restart"
        STOPSERVICE="eval /etc/init.d/\$service stop"
    fi

    ENABLESERVICE="eval update-rc.d \$service defaults"
    DISABLESERVICE="eval update-rc.d \$service disable"

    # Variables globales
    DEFAULTDEV=""
    NGINX_TEMPLATE="$INSTALL_TARGET/etc/nginxServer.conf.tmpl"
    NGINX_OUTPUT="/etc/nginx/sites-available/ogdhcp.conf"
    NGINX_CONF_PATH="/etc/nginx/nginx.conf"
    PHP_FPM_CONF_PATH="/etc/php/__PHPVERSION__/fpm/pool.d/www.conf"
    NEW_FPM_CONF_PATH="/etc/php/__PHPVERSION__/fpm/pool.d/ogdhcp.conf"
    SOCKET_PATH="/run/php/php__PHPVERSION__-fpm-ogdhcp.sock"

    # Registro de incidencias.
    OGLOGFILE="$INSTALL_TARGET/var/log/${PROGRAMNAME%.sh}.log"
    LOG_FILE="/tmp/$(basename "$OGLOGFILE")"
}


function checkDependencies() {
    echoAndLog "Checking dependencies..."

    # Lista de dependencias
    local DEPENDENCIES=(
        php
        php-cli
        php-fpm
        php-json
        php-pdo
        php-mysql
        php-zip
        php-gd
        php-mbstring
        php-curl
        php-xml
        php-pear
        php-bcmath
        composer
        unzip
        kea-dhcp4-server
        kea-common
        kea-ctrl-agent
        jq
        net-tools
        nginx
        
    )
    export DEBIAN_FRONTEND=noninteractive
    apt-get update -y
    # Comprobar cada dependencia
    for dep in "${DEPENDENCIES[@]}"; do
        if ! dpkg -s "$dep" >/dev/null 2>&1; then
            echoAndLog "$dep is not installed. Installing..."
            apt-get install -y --no-install-recommends "$dep"
        else
            echoAndLog "$dep is already installed."
        fi
    done
    sed -i '/ConditionFileNotEmpty=\/etc\/kea\/kea-api-password/d' /usr/lib/systemd/system/kea-ctrl-agent.service
    chown -R _kea:_kea /etc/kea
    systemctl daemon-reload
    systemctl restart kea-ctrl-agent.service
    echoAndLog "Dependencies checked."
}


# Obtiene el código fuente del proyecto desde el repositorio de GitHub.
function downloadCode() {
    if [ $# -ne 1 ]; then
        errorAndLog "${FUNCNAME}(): invalid number of parameters"
        exit 1
    fi

    local url="$1"

    echoAndLog "${FUNCNAME}(): downloading code from '$url'..."

    GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=accept-new" git archive --remote="$url" --format zip --output opengnsys.zip --prefix=opengnsys/ "$BRANCH" && unzip opengnsys.zip
    if [ $? -ne 0 ]; then
        errorAndLog "${FUNCNAME}(): error getting OpenGnsys code from $url"
        return 1
    fi
    rm -f opengnsys.zip
    echoAndLog "${FUNCNAME}(): code was downloaded"
    return 0
}

# Crea la estructura base de la instalación de opengnsys
function createDirs() {
    if [ $# -ne 1 ]; then
        errorAndLog "${FUNCNAME}(): invalid number of parameters"
        exit 1
    fi

    local path_opengnsys_base="$1"

    # Crear estructura de directorios.
    echoAndLog "${FUNCNAME}(): creating directory paths in $path_opengnsys_base"
    mkdir -p "$path_opengnsys_base"/{bin,config,docs,public,src,etc/kea/backup,templates,var/{cache,log},vendor}
    if [ $? -ne 0 ]; then
        errorAndLog "${FUNCNAME}(): error while creating dirs. Do you have write permissions?"
        return 1
    fi

    # Crear usuario ficticio.
    if id -u "$OPENGNSYS_CLIENT_USER" &>/dev/null; then
        echoAndLog "${FUNCNAME}(): user \"$OPENGNSYS_CLIENT_USER\" is already created"
    else
        echoAndLog "${FUNCNAME}(): creating OpenGnsys user"
        useradd "$OPENGNSYS_CLIENT_USER" 2>/dev/null
        if [ $? -ne 0 ]; then
            errorAndLog "${FUNCNAME}(): error creating OpenGnsys user"
            return 1
        fi
    fi

    # Mover el fichero de registro de instalación al directorio de logs.
    echoAndLog "${FUNCNAME}(): moving installation log file"
    mv "$LOG_FILE" "$path_opengnsys_base/var/log" && LOG_FILE="$OGLOGFILE"
    chmod 777 "$LOG_FILE"
    sudo chmod -R 777 "$path_opengnsys_base/etc"

    # Mover el fichero de registro de instalación al directorio de logs.
    echoAndLog "${FUNCNAME}(): moving installation log file"
    touch "$path_opengnsys_base/var/log/dev.log"
    chmod 777 "$path_opengnsys_base/var/log/dev.log"

    echoAndLog "${FUNCNAME}(): directory paths created"
    return 0

    # Cambiar permisos de usuario
    echoAndLog "Changing user permission"
    chown -R "$OPENGNSYS_CLIENT_USER:$OPENGNSYS_CLIENT_USER" "$INSTALL_TARGET"
}


function create_ogdhcp_project {
    # Crea el usuario ogdhcp si no existe
    local path_opengnsys_base="$1"
    if ! id -u opengnsys &>/dev/null; then
        echoAndLog "${FUNCNAME}(): creating opengnsys user"
        useradd opengnsys 2>/dev/null
        if [ $? -ne 0 ]; then
            errorAndLog "${FUNCNAME}(): error creating opengnsys user"
            return 1
        fi
    fi

    # Crea el directorio path_opengnsys_base con el usuario opengnsys
    echoAndLog "${FUNCNAME}(): creating directory $path_opengnsys_base with opengnsys user"
    sudo mkdir -p "$path_opengnsys_base"
    sudo chown opengnsys:opengnsys $path_opengnsys_base
    if [ $? -ne 0 ]; then
        errorAndLog "${FUNCNAME}(): error while creating directory $path_opengnsys_base"
        return 1
    fi

    echoAndLog "Directory $path_opengnsys_base created with opengnsys user"
}

function copyServerFiles() {
    if [ $# -ne 1 ]; then
        errorAndLog "${FUNCNAME}(): invalid number of parameters"
        exit 1
    fi

    local path_opengnsys_base="$1"

    # Lista de ficheros y directorios origen y de directorios destino.
    local SOURCES=(
        config
        #public
        src
        etc
        .env
        composer.json
        composer.lock
        phpunit.xml.dist
        symfony.lock
    )
    local TARGETS=(
        config
        #public
        src
        etc
        .env
        composer.json
        composer.lock
        phpunit.xml.dist
        symfony.lock
    )

    if [ "${#SOURCES[@]}" != "${#TARGETS[@]}" ]; then
        errorAndLog "${FUNCNAME}(): inconsistent number of array items"
        exit 1
    fi

    # Copiar ficheros.
    echoAndLog "${FUNCNAME}(): copying files to server directories"

    pushd "$WORKDIR/ogdhcp" || return
    local i
    for (( i = 0; i < ${#SOURCES[@]}; i++ )); do
        if [ -f "${SOURCES[$i]}" ]; then
            echoAndLog "Copying ${SOURCES[$i]} to $path_opengnsys_base/${TARGETS[$i]}"
            cp -a "${SOURCES[$i]}" "$path_opengnsys_base/${TARGETS[$i]}"
        elif [ -d "${SOURCES[$i]}" ]; then
            echoAndLog "Copying content of ${SOURCES[$i]} to $path_opengnsys_base/${TARGETS[$i]}"
            cp -a "${SOURCES[$i]}"/* "$path_opengnsys_base/${TARGETS[$i]}"
        else
            warningAndLog "Unable to copy ${SOURCES[$i]} to $path_opengnsys_base/${TARGETS[$i]}"
        fi
    done
    echoAndLog "Changing user permission"
    chown -R "$OPENGNSYS_CLIENT_USER:$OPENGNSYS_CLIENT_USER" "$INSTALL_TARGET"

    popd || return
}


function runComposer() {
    echoAndLog "Running composer.phar to install dependencies..."
    local path_opengnsys_base="$1"
    pushd $path_opengnsys_base
    pwd
    # Ejecutar composer.phar
    sudo -u "$OPENGNSYS_CLIENT_USER" composer --no-interaction install

    # Comprobar si la ejecución fue exitosa
    if [ $? -ne 0 ]; then
        errorAndLog "Failed to run composer.phar"
        popd
        return 1
    fi


    echoAndLog "composer.phar ran successfully and dependencies were installed"
    return 0
}


get_first_network_interface_with_traffic() {
    while read -r line; do
        if [[ "$line" == *:* ]]; then
            interface=$(echo "$line" | cut -d ':' -f 1 | xargs)
            if [[ "$interface" != "lo" ]]; then
                received_bytes=$(echo "$line" | awk '{print $2}')
                transmitted_bytes=$(echo "$line" | awk '{print $10}')
                if (( received_bytes > 0 || transmitted_bytes > 0 )); then
                    DEFAULTDEV="$interface"
                    break
                fi
            fi
        fi
    done < /proc/net/dev
}
comment_auth_kea() {
    KEA_CTRL_AGENT_CONF="/etc/kea/kea-ctrl-agent.conf"
    
    # Verificar si el bloque de "authentication" ya está comentado
    if grep -q '^[^#]*"authentication": {' "$KEA_CTRL_AGENT_CONF"; then
        echo "Comentando el bloque de autenticación en $KEA_CTRL_AGENT_CONF..."

        # Comentar solo el bloque de authentication desde la apertura hasta la línea con '},'
        sed -i '/"authentication": {/,/^[[:space:]]*},/ {
            s/^\([[:space:]]*\)\([^#]\)/\1#\2/
        }' "$KEA_CTRL_AGENT_CONF"

        echo "Bloque de autenticación comentado correctamente."
    else
        echo "El bloque de autenticación ya está comentado."
    fi

    # Verificar si el bloque fue comentado correctamente
    if grep -q '^#\s*"authentication": {' "$KEA_CTRL_AGENT_CONF"; then
        echo "Confirmación: Bloque de autenticación comentado correctamente."

        # Reiniciar el servicio de Kea Control Agent para aplicar los cambios
        echo "Reiniciando el agente de Kea Control Agent..."
        sudo systemctl restart kea-ctrl-agent.service

        if systemctl is-active --quiet kea-ctrl-agent.service; then
            echo "El agente de Kea Control Agent se ha reiniciado correctamente."
        else
            echo "Error: No se pudo reiniciar el agente de Kea Control Agent."
        fi
    else
        echo "Error: No se pudo comentar correctamente el bloque de autenticación."
    fi
}
# Función para obtener la dirección IP de una interfaz
get_ip_address() {
    local interface="$1"
    ip -4 addr show "$interface" | grep -oP "(?<=inet\s)\d+(\.\d+){3}"
}

# Función para obtener la versión de PHP instalada
get_php_fpm_version() {
    php -v | grep -oP "PHP \K\d+\.\d+"
}
add_write_permission_apparmor() {
    APPARMOR_PROFILE="/etc/apparmor.d/usr.sbin.kea-dhcp4"

    # Comprobar si las líneas existen
    if grep -q "/etc/kea/ r," "$APPARMOR_PROFILE" && grep -q "/etc/kea/** r," "$APPARMOR_PROFILE"; then
        echo "Modificando permisos en $APPARMOR_PROFILE..."

        # Modificar las líneas /etc/kea/ r, y /etc/kea/** r, añadiendo w
        sed -i 's#/etc/kea/ r,#/etc/kea/ rw,#g' "$APPARMOR_PROFILE"
        sed -i 's#/etc/kea/\*\* r,#/etc/kea/** rw,#g' "$APPARMOR_PROFILE"

        echo "Permisos de escritura añadidos correctamente a $APPARMOR_PROFILE."
    else
        echo "Las líneas no fueron encontradas o ya están modificadas."
    fi

    # Recargar el perfil de AppArmor para aplicar los cambios
    echo "Recargando el perfil de AppArmor para kea-dhcp4..."
    sudo apparmor_parser -r "$APPARMOR_PROFILE"
    
    if [ $? -eq 0 ]; then
        echo "El perfil de AppArmor se recargó correctamente."
    else
        echo "Error al recargar el perfil de AppArmor."
    fi
}


# Función para configurar Nginx
setup_nginx() {
    local path_opengnsys_base="$1"
    local public_dir="$path_opengnsys_base/public"
    #ip_address_server=$(get_ip_address "$DEFAULTDEV")
    if [[ ! -f "$CONFIG_FILE" ]]; then
        echo "Error: El archivo de configuración no se encontró."
        exit 1
    fi

    ip_address_server=$(jq -r '.ogDhcpIP' "$CONFIG_FILE")

    if [[ -z "$ip_address_server" ]]; then
        echo "Error: No se pudo obtener la dirección IP del servidor desde el archivo de configuración."
        exit 1
    fi
    php_version=$(get_php_fpm_version)

    if [[ -z "$php_version" ]]; then
        echo "Error: No se pudo obtener la versión de PHP."
        exit 1
    fi

    # Leer y modificar la plantilla de configuración de nginx
    if [[ ! -f "$NGINX_TEMPLATE" ]]; then
        echo "Error: La plantilla de Nginx no se encontró."
        exit 1
    fi
    
    nginx_content=$(<"$NGINX_TEMPLATE")
    nginx_content="${nginx_content//__SERVERIP__/$ip_address_server}"
    nginx_content="${nginx_content//__PHPVERSION__/$php_version}"
    nginx_content="${nginx_content//__PUBLICDIR__/$public_dir}"

    # Crear el archivo de configuración de Nginx
    echo "$nginx_content" > "$NGINX_OUTPUT"
    echo "Archivo de configuración de Nginx creado en $NGINX_OUTPUT."

    # Crear el enlace simbólico
    ln -sf "$NGINX_OUTPUT" /etc/nginx/sites-enabled/ogdhcp.conf
    echo "Enlace simbólico creado en /etc/nginx/sites-enabled/ogdhcp.conf."

    # Modificar nginx.conf para ejecutar como opengnsys
    sed -i 's/user www-data;/user opengnsys;/g' "$NGINX_CONF_PATH"
    echo "Nginx configurado para ejecutarse como opengnsys."

    # Reiniciar Nginx
    systemctl restart nginx.service
    echo "Servicio Nginx reiniciado."
}

# Función para modificar el archivo de configuración PHP-FPM
modify_php_fpm_config() {
    php_version=$(get_php_fpm_version)

    if [[ -z "$php_version" ]]; then
        echo "Error: No se pudo obtener la versión de PHP."
        exit 1
    fi

    php_fpm_conf_path="/etc/php/$php_version/fpm/pool.d/www.conf"
    new_fpm_conf_path="/etc/php/$php_version/fpm/pool.d/ogdhcp.conf"
    socket_path="/run/php/php$php_version-fpm-ogdhcp.sock"

    # Verificar si el archivo new_fpm_conf_path ya existe
    if [[ -f "$new_fpm_conf_path" ]]; then
        echo "El archivo $new_fpm_conf_path ya existe. No se realizarán modificaciones."
        return
    fi

    # Copiar el archivo www.conf a opengnsys.conf
    cp "$php_fpm_conf_path" "$new_fpm_conf_path"

    # Modificar el archivo ogdhcp.conf
    sed -i 's/\[www\]/[ogdhcp]/g' "$new_fpm_conf_path"
    sed -i 's/user = www-data/user = opengnsys/g' "$new_fpm_conf_path"
    sed -i 's/group = www-data/group = opengnsys/g' "$new_fpm_conf_path"
    sed -i "s|listen =.*|listen = $socket_path|g" "$new_fpm_conf_path"
    sed -i 's/listen.owner = www-data/listen.owner = opengnsys/g' "$new_fpm_conf_path"
    sed -i 's/listen.group = www-data/listen.group = opengnsys/g' "$new_fpm_conf_path"

    # Reiniciar PHP-FPM
    systemctl restart php"$php_version"-fpm.service
    echo "PHP-FPM reiniciado."

    # Verificar la creación del socket
    if [[ -S "$socket_path" ]]; then
        echo "Socket PHP-FPM $socket_path creado correctamente."
    else
        echo "Error: El socket PHP-FPM $socket_path no se ha creado."
        exit 1
    fi
}


configure_kea() {

    # Verificar si jq está instalado
    if ! command -v jq &> /dev/null; then
        echo "jq no está instalado. Por favor, instala jq para continuar."
        exit 1
    fi

    # Verificar si el archivo de configuración existe
    if [ ! -f "$CONFIG_FILE" ]; then
        echo "El archivo $CONFIG_FILE no se encuentra. Asegúrate de que esté disponible antes de la instalación."
        exit 1
    fi

    # Leer los parámetros del archivo JSON usando jq
    INTERFACES=$(jq -r '.interfaces[]' "$CONFIG_FILE")
    OGBOOT_IP=$(jq -r '.ogbootIP' "$CONFIG_FILE")

    # Crear la configuración mínima de Kea DHCP
    KEA_CONFIG="/etc/kea/kea-dhcp4.conf"

    # Hacer una copia de seguridad del archivo kea-dhcp4.conf si ya existe
    if [ -f "$KEA_CONFIG" ]; then
        cp "$KEA_CONFIG" "$KEA_CONFIG.backup"
        echo "Se ha creado una copia de seguridad del archivo de configuración actual en $KEA_CONFIG.backup"
    fi

    # Generar la configuración mínima para Kea DHCP
    cat > "$KEA_CONFIG" << EOL
{
    "Dhcp4": {
        "interfaces-config": {
            "interfaces": [ $(
                for interface in $INTERFACES; do
                    echo "\"$interface\""
                done | paste -sd "," -
            ) ]
        },
        "client-classes": [
            {
                "name": "UEFI-64",
                "test": "not substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'",
                "boot-file-name": "ipxe.efi",
                "next-server": "$OGBOOT_IP"
            },
            {
                "name": "Legacy",
                "test": "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'",
                "boot-file-name": "undionly.kpxe",
                "next-server": "$OGBOOT_IP"
            }
        ],
        "control-socket": {
            "socket-name": "/run/kea/kea4-ctrl-socket",
            "socket-type": "unix"
        }
    }
}
EOL

    echo "Se ha generado la configuración mínima de Kea DHCP en $KEA_CONFIG"
    # Reiniciar el servicio de Kea DHCP y verificar si se reinicia correctamente
    echo "Reiniciando el servicio Kea DHCP..."
    sudo systemctl restart kea-dhcp4-server.service

    # Comprobar el estado del servicio Kea DHCP
    if systemctl is-active --quiet kea-dhcp4-server.service; then
        echo "Kea DHCP reiniciado correctamente."
    else
        echo "Error al reiniciar Kea DHCP."
        exit 1
    fi
}



#####################################################################
####### Algunas funciones útiles de propósito general:
#####################################################################

# Obtiene la fecha y hora actual en el formato especificado
function getDateTime() {
    date "+%Y%m%d-%H%M%S"
}

# Escribe un mensaje en el archivo de registro y lo muestra por pantalla
function echoAndLog() {
    local DATETIME=$(getDateTime)
    echo "$1"
    echo "$DATETIME;$SSH_CLIENT;$1" >> "$LOG_FILE"
}

# Escribe un mensaje de error en el archivo de registro y lo muestra por pantalla
function errorAndLog() {
    local DATETIME=$(getDateTime)
    echo "ERROR: $1"
    echo "$DATETIME;$SSH_CLIENT;ERROR: $1" >> "$LOG_FILE"
}

# Escribe un mensaje de advertencia en el archivo de registro y lo muestra por pantalla
function warningAndLog() {
    local DATETIME=$(getDateTime)
    echo "Warning: $1"
    echo "$DATETIME;$SSH_CLIENT;Warning: $1" >> "$LOG_FILE"
}

##########################################################################
################################main######################################

# Sólo ejecutable por usuario root
if [ "$(whoami)" != 'root' ]; then
		echo "ERROR: this program must run under root privileges!!"
		exit 1
fi

globalSetup

echoAndLog "OpenGnsys installation begins at $(date)"

mkdir -p $WORKDIR
pushd $WORKDIR

checkDependencies
# Si es necesario, descarga el repositorio de código en directorio temporal
if [ $REMOTE -eq 1 ]; then
	downloadCode $GIT_REPO
	if [ $? -ne 0 ]; then
		errorAndLog "Error while getting code from the repository"
		exit 1
	fi
else
	ln -fs "$(dirname $PROGRAMDIR)" ogdhcp
fi

create_ogdhcp_project ${INSTALL_TARGET}
if [ $? -ne 0 ]; then
        errorAndLog "Error while creating skeleton directory!"
        exit 1
fi

# Arbol de directorios de OpenGnsys.
createDirs ${INSTALL_TARGET}
if [ $? -ne 0 ]; then
	errorAndLog "Error while creating directory paths!"
	exit 1
fi


# Copiar ficheros de servicios OpenGnsys Server.
copyServerFiles ${INSTALL_TARGET}
if [ $? -ne 0 ]; then
	errorAndLog "Error while copying the server files!"
	exit 1
fi


comment_auth_kea
if [ $? -ne 0 ]; then
	errorAndLog "Error while commenting auth block!"
	exit 1
fi


runComposer ${INSTALL_TARGET}
setup_nginx $INSTALL_TARGET
if [ $? -ne 0 ]; then
	errorAndLog "Error configuring Nginx for OpenGnsys Admin"
	exit 1
fi
modify_php_fpm_config
if [ $? -ne 0 ]; then
    errorAndLog "Error configuring PHP-FPM for OpenGnsys Admin"
    exit 1
fi

add_write_permission_apparmor
if [ $? -ne 0 ]; then
    errorAndLog "Error adding write permission to AppArmor profile"
    exit 1
fi

configure_kea
if [ $? -ne 0 ]; then
    errorAndLog "Error configuring Kea DHCP initial configuration"
    exit 1
fi


# install_kea
# install_php
# install_composer
# install_symfony
# install_swagger
# Ahora puedes clonar e instalar el componente ogDhcp
# git clone <URL del repositorio de ogDhcp>
# cd <directorio de ogDhcp>
# composer install
