#!/bin/bash

# ======================================================================
# Archivo:       createiso
# Ruta:          /usr/local/bin/
# Autor:         Charlie Martínez® <cmartinez@quirinux.org>
# Licencia:      https://www.gnu.org/licenses/gpl-2.0.txt
# Propósito:     Crear iso a partir de distro master
# Distribución:  Quirinux 2.x
# ======================================================================

#
# Copyright (c) 2019-2025 Charlie Martínez, derechos reservados.
# Licencia: https://www.gnu.org/licenses/gpl-2.0.txt
# Usos autorizados y no autorizados de la marca Quirinux:
# Ver https://www.quirinux.org/aviso-legal
#

#
# Este script automatiza la creación de una iso de Quirinux a partir del
# master, usando "coa remaster" (Penguins' Eggs, de Piero Proietti).
#
# El branding de Calamares (show.qml, branding.desc, imágenes) ya no se
# toma del antiguo ".wardrobe/vendors", sino de /etc/penguins-eggs.d/branding,
# donde lo deja "tailor wear quirinux". Por eso, antes de usar este script,
# el master debe haber sido vestido con tailor.
#
# Uso:
#   createiso [--test] [-f] [-t DIRECTORIO]
#
#   --test            Simula: muestra qué cambiaría, sin tocar nada ni
#                     crear la iso (tampoco ejecuta cleanmaster).
#   -f                Compresión rápida (lz4): iso más grande, para pruebas.
#                     Sin -f se usa la compresión por defecto de coa.
#   -t DIRECTORIO     Deja la iso en ese directorio (--target-dir de coa).
#

# ----------------------------------------------------------------------
# Argumentos
# ----------------------------------------------------------------------

TEST_MODE=0
FAST_MODE=0
TARGET_DIR=""

while [[ $# -gt 0 ]]; do
    case "$1" in
        --test)
            TEST_MODE=1
            ;;
        -f)
            FAST_MODE=1
            ;;
        -t|--target-dir)
            TARGET_DIR="$2"
            shift
            ;;
        *)
            echo "Opción desconocida: $1"
            echo "Uso: createiso [--test] [-f] [-t DIRECTORIO]"
            exit 1
            ;;
    esac
    shift
done

if [[ "$TEST_MODE" -eq 1 ]]; then
    echo ">>> MODO TEST ACTIVADO (no se modificarán archivos ni se creará la ISO)"
    echo
fi

# ----------------------------------------------------------------------
# Variables
# ----------------------------------------------------------------------

source "/opt/createmaster/var_distro"     # Variables de la distribución
source "/opt/createmaster/functions"      # Funciones globales

# Directorio de trabajo por defecto de coa (donde deja la iso si no se usa -t)
EGGS_WORKDIR="/home/eggs"

# show.qml que tailor instaló en el master. Es la copia que coa incorpora
# a la iso y que Calamares mostrará durante la instalación.
ARCHIVO_QML="/etc/penguins-eggs.d/branding/calamares/branding/show.qml"

# Directorio donde buscar los kernels instalados
BOOT_DIR="${BOOT_DIR:-/boot}"

EGGS_BIN=""
MARKER=""

# Usuario normal que ejecutó el script y su escritorio (los rellena _destino)
USER_NAME=""
USER_HOME=""
USER_DESKTOP=""

# ----------------------------------------------------------------------
# Funciones locales
# ----------------------------------------------------------------------

function _prechecks() {

    # Se comprueba todo ANTES de ejecutar cleanmaster, que es destructivo.

    # Binario: "coa" (o "eggs" si es el nuevo y soporta "remaster").
    # El antiguo eggs (node) no tiene "remaster", por eso se prueba.
    local CANDIDATO
    for CANDIDATO in coa eggs; do
        if command -v "$CANDIDATO" &>/dev/null \
            && "$CANDIDATO" remaster --help &>/dev/null; then
            EGGS_BIN="$CANDIDATO"
            break
        fi
    done

    if [[ -z "$EGGS_BIN" ]]; then
        echo "Error: no se encontró un 'coa' (o 'eggs') con el comando 'remaster'."
        exit 1
    fi

    if [[ ! -f "$ARCHIVO_QML" ]]; then
        echo "Error: no existe $ARCHIVO_QML"
        echo "Primero hay que vestir el master con: tailor wear $DISTRO_NAME"
        exit 1
    fi

    if [[ -n "$TARGET_DIR" && ! -d "$TARGET_DIR" ]]; then
        echo "Error: el directorio de destino no existe: $TARGET_DIR"
        exit 1
    fi

    echo "Usando: $EGGS_BIN remaster"
}

# ----------------------------------------------------------------------

function _kernelcheck() {

    # coa remaster copia a la iso el kernel EN EJECUCIÓN (uname -r).
    # Si en el master hay instalado uno más nuevo, la iso saldría con el viejo.

    local running latest RESPUESTA

    running=$(uname -r)
    latest=$(ls -1 "$BOOT_DIR"/vmlinuz-* 2>/dev/null \
        | sed 's|.*/vmlinuz-||' | sort -V | tail -n1)

    if [[ -z "$latest" ]]; then
        echo "Error: no se pudo determinar la versión más reciente del kernel."
        exit 1
    fi

    if [[ "$running" == "$latest" ]]; then
        return
    fi

    echo "AVISO: el kernel en ejecución ($running) no es el más reciente instalado ($latest)."
    echo "       La iso incluirá el kernel en ejecución. Para usar el nuevo,"
    echo "       reinicia el master con ese kernel y vuelve a ejecutar createiso."

    [[ "$TEST_MODE" -eq 1 ]] && return

    echo "¿Continuar de todos modos? [s/N]"
    read -r RESPUESTA
    [[ "$RESPUESTA" =~ ^[sS]$ ]] || exit 1
}

# ----------------------------------------------------------------------

function _user_valido() {

    # Un usuario es válido si existe, no es root (UID >= UID_MIN, normalmente
    # 1000) y tiene directorio personal. Devuelve su home por stdout.

    local U="$1" UID_USER HOME_USER UID_MIN

    [[ -n "$U" ]] || return 1

    UID_USER=$(id -u "$U" 2>/dev/null) || return 1
    UID_MIN=$(awk '$1=="UID_MIN"{print $2}' /etc/login.defs 2>/dev/null)
    [[ "$UID_MIN" =~ ^[0-9]+$ ]] || UID_MIN=1000
    [[ "$UID_USER" -ge "$UID_MIN" ]] || return 1

    HOME_USER=$(getent passwd "$U" | cut -d: -f6)
    [[ -d "$HOME_USER" ]] || return 1

    echo "$HOME_USER"
}

# ----------------------------------------------------------------------

function _destino() {

    # Averigua quién es el usuario normal que ejecutó el script (aunque ahora
    # sea root, sin sudo) y dónde está su escritorio, que es donde se dejarán
    # la iso y el .md5. Se hace ANTES de cleanmaster para fallar temprano.

    local CANDIDATOS=() CAND UIDN TTY_ACTUAL CONF LINEA RUTA D

    # 1) sudo (por si algún día se ejecuta con sudo)
    [[ -n "$SUDO_USER" ]] && CANDIDATOS+=("$SUDO_USER")

    # 2) Usuario de la sesión de login original: lo fija PAM y lo heredan
    #    "su", "su -" y "su root", así que sobrevive al cambio a root.
    if [[ -r /proc/self/loginuid ]]; then
        UIDN=$(< /proc/self/loginuid)
        if [[ "$UIDN" =~ ^[0-9]+$ && "$UIDN" -ne 4294967295 ]]; then
            CANDIDATOS+=("$(getent passwd "$UIDN" | cut -d: -f1)")
        fi
    fi

    # 3) Dueño de la terminal desde la que se ejecuta el script
    TTY_ACTUAL=$(tty 2>/dev/null)
    [[ -e "$TTY_ACTUAL" ]] && CANDIDATOS+=("$(stat -c %U "$TTY_ACTUAL")")

    # 4) logname / who am i
    CANDIDATOS+=("$(logname 2>/dev/null)")
    CANDIDATOS+=("$(who am i 2>/dev/null | awk '{print $1}')")

    # 5) Usuario del master: nombre de la distro en minúsculas + "2"
    CANDIDATOS+=("${DISTRO_NAME,,}2")

    # 6) Primer usuario normal que exista
    CANDIDATOS+=("$(getent passwd | awk -F: '$3>=1000 && $3<60000 && $6 ~ /^\/home\// {print $1; exit}')")

    for CAND in "${CANDIDATOS[@]}"; do
        if USER_HOME=$(_user_valido "$CAND"); then
            USER_NAME="$CAND"
            break
        fi
    done

    if [[ -z "$USER_NAME" ]]; then
        echo "Error: no se pudo determinar el usuario normal que ejecutó el script."
        exit 1
    fi

    # Escritorio: primero el que declara el usuario en user-dirs.dirs
    # (se lee como texto; no se hace "source" de un archivo del usuario
    # estando como root).
    CONF="$USER_HOME/.config/user-dirs.dirs"
    if [[ -f "$CONF" ]]; then
        LINEA=$(grep -E '^[[:space:]]*XDG_DESKTOP_DIR=' "$CONF" | tail -n1)
        RUTA="${LINEA#*=}"
        RUTA="${RUTA%\"}"
        RUTA="${RUTA#\"}"
        RUTA="${RUTA/#\$HOME/$USER_HOME}"
        RUTA=$(realpath -m "$RUTA" 2>/dev/null)
        if [[ "$RUTA" == "$USER_HOME"/* && -d "$RUTA" ]]; then
            USER_DESKTOP="$RUTA"
        fi
    fi

    # Si no, cualquier carpeta de escritorio que ya exista (sin importar
    # mayúsculas/minúsculas: Escritorio, escritorio, Desktop...)
    if [[ -z "$USER_DESKTOP" ]]; then
        for D in Escritorio Desktop; do
            RUTA=$(find "$USER_HOME" -maxdepth 1 -type d -iname "$D" 2>/dev/null | head -n1)
            if [[ -n "$RUTA" ]]; then
                USER_DESKTOP="$RUTA"
                break
            fi
        done
    fi

    # Último recurso: no se crea ninguna carpeta nueva; se usa el home.
    if [[ -z "$USER_DESKTOP" ]]; then
        echo "AVISO: no se encontró el escritorio de $USER_NAME; se usará $USER_HOME"
        USER_DESKTOP="$USER_HOME"
    fi

    echo "Usuario: $USER_NAME - la iso se guardará en: $USER_DESKTOP"
}

# ----------------------------------------------------------------------

function _rev() {

    local REV_ACTUAL=0

    if grep -q "Rev\." "$ARCHIVO_QML"; then
        REV_ACTUAL=$(grep -oP 'Rev\. \d+' "$ARCHIVO_QML" | head -n1 | awk '{print $2}')
        [[ "$REV_ACTUAL" =~ ^[0-9]+$ ]] || REV_ACTUAL=0
    fi

    echo "Número de revisión actual: $REV_ACTUAL"
    echo "¿Mantener este número? [s/n]"
    read -r RESPUESTA

    if [[ "$RESPUESTA" =~ ^[sS]$ ]]; then
        REVISION="$REV_ACTUAL"
    else
        echo "Introduce el nuevo número de revisión (0 = sin revisión):"
        read -r REVISION
        [[ "$REVISION" =~ ^[0-9]+$ ]] || REVISION=0
    fi

    if [[ "$REVISION" -gt 0 ]]; then
        FECHA_ACTUAL=$(date +"%d-%m-%Y")
    else
        FECHA_ACTUAL=""
    fi
}

# ----------------------------------------------------------------------

function _calamares() {

    # Actualiza el título y la línea "Versión:" de show.qml. Formato:
    #   <h1>Quirinux GNU/Linux Versión 2.2</h1>
    #   <b>Versión:</b> 2.2 Rev. 2 - 04-09-2026 <br/><br/>

    local NOMBRE="${DISTRO_NAME^}"
    local TITULO="<h1>$NOMBRE GNU/Linux Versión $DISTRO_VERSION</h1>"
    local VERSION_FINAL

    if [[ "$REVISION" -gt 0 ]]; then
        VERSION_FINAL="<b>Versión:</b> $DISTRO_VERSION Rev. $REVISION - $FECHA_ACTUAL"
    else
        VERSION_FINAL="<b>Versión:</b> $DISTRO_VERSION - $DISTRO_DATE_RELEASE"
    fi

    echo "Procesando: $ARCHIVO_QML"

    local SED_ARGS=(
        -e "s@<h1>[^<]*</h1>@$TITULO@g"
        -e "s@<b>Versión:</b>[^<]*<br/><br/>@$VERSION_FINAL <br/><br/>@g"
    )

    if [[ "$TEST_MODE" -eq 1 ]]; then
        echo "---- SIMULACIÓN (diferencias) ----"
        diff <(cat "$ARCHIVO_QML") <(sed "${SED_ARGS[@]}" "$ARCHIVO_QML")
        echo "----------------------------------"
        return
    fi

    sed -i "${SED_ARGS[@]}" "$ARCHIVO_QML"

    # Comprobar que el cambio se aplicó (por si cambia el formato de show.qml)
    if ! grep -qF "$VERSION_FINAL <br/><br/>" "$ARCHIVO_QML" \
        || ! grep -qF "$TITULO" "$ARCHIVO_QML"; then
        echo "Error: no se pudo actualizar la versión en $ARCHIVO_QML."
        echo "Revisa que conserve las líneas <h1>...</h1> y <b>Versión:</b> ... <br/><br/>"
        exit 1
    fi
}

# ----------------------------------------------------------------------

function _createiso() {

    local ARGS=(remaster)

    [[ "$FAST_MODE" -eq 1 ]] && ARGS+=(--compression lz4)
    [[ -n "$TARGET_DIR" ]]   && ARGS+=(--target-dir "$TARGET_DIR")

    if [[ "$TEST_MODE" -eq 1 ]]; then
        echo "TEST: se ejecutaría: $EGGS_BIN ${ARGS[*]}"
        return
    fi

    # Marca de tiempo para localizar luego la iso recién creada
    MARKER=$(mktemp)

    if ! "$EGGS_BIN" "${ARGS[@]}"; then
        echo "Error: '$EGGS_BIN ${ARGS[*]}' terminó con errores."
        rm -f "$MARKER"
        exit 1
    fi
}

# ----------------------------------------------------------------------

function _isoname() {

    [[ "$TEST_MODE" -eq 1 ]] && return

    local DESTINO="${TARGET_DIR:-$EGGS_WORKDIR}"

    ISO_ORIGINAL=$(find "$DESTINO" -maxdepth 1 -name "*.iso" -newer "$MARKER" \
        -printf '%T@ %p\n' | sort -n | tail -n 1 | cut -d' ' -f2-)
    rm -f "$MARKER"

    if [[ -z "$ISO_ORIGINAL" ]]; then
        echo "Error: coa terminó, pero no se encontró una iso nueva en $DESTINO"
        exit 1
    fi

    ISO_NOMBRE="${DISTRO_NAME}-${DISTRO_VERSION}-${DISTRO_RELEASE^}_x64"
    [[ "$REVISION" -gt 0 ]] && ISO_NOMBRE="${ISO_NOMBRE}_Rev${REVISION}"
    ISO_NOMBRE="${ISO_NOMBRE}.iso"

    mv "$ISO_ORIGINAL" "$USER_DESKTOP/$ISO_NOMBRE"
    chown "$USER_NAME:" "$USER_DESKTOP/$ISO_NOMBRE"
}

# ----------------------------------------------------------------------

function _md5sum() {

    [[ "$TEST_MODE" -eq 1 ]] && return

    ISO_BASE="${ISO_NOMBRE%.iso}"
    if [ -e "$USER_DESKTOP/$ISO_NOMBRE" ]; then
        cd "$USER_DESKTOP" || exit 1
        md5sum "$ISO_NOMBRE" > "${ISO_BASE}.md5"
        chown "$USER_NAME:" "${ISO_BASE}.md5"
    fi
}

# ----------------------------------------------------------------------

function _bye() {

    if [[ "$TEST_MODE" -eq 1 ]]; then
        echo
        echo ">>> FIN DEL TEST: no se modificó ningún archivo"
        return
    fi

    if [ -e "$USER_DESKTOP/${ISO_BASE}.md5" ]; then
        echo "¡ISO Creada! Almacenada en $USER_DESKTOP"
    fi
}

# ======================================================================
# Ejecución de funciones
# ======================================================================
_rootcheck          # Global    # /opt/createmaster/functions
_internet           # Global    # /opt/createmaster/functions
_prechecks          # Local
_destino            # Local
_kernelcheck        # Local

if [[ "$TEST_MODE" -eq 1 ]]; then
    echo "TEST: se ejecutaría cleanmaster"
else
    cleanmaster     # /usr/local/bin/cleanmaster
fi

_rev                # Local
_calamares          # Local
_createiso          # Local
_isoname            # Local
_md5sum             # Local
_bye                # Local
