#!/usr/bin/env bash
# ============================================================================
# install-pxe-server.sh
# Automated PXE boot server setup — installs dnsmasq, TFTP, and nginx,
# configures PXE boot menus, generates Kickstart and Preseed templates
#
# Author:  Phil Connor
# Contact: contact@mylinux.work
# License: MIT
# Version: 1.0.0
# ============================================================================

set -uo pipefail

# ============================================================================
# Defaults
# ============================================================================
INTERFACE=""
DHCP_RANGE="10.0.0.100,10.0.0.200"
TFTP_ROOT="/srv/tftp"
HTTP_ROOT="/var/www/pxe"
DISTROS="rocky9"
SERVER_IP=""
OS_FAMILY=""
OS_ID=""
OS_VERSION=""

# ============================================================================
# Colour output
# ============================================================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

log_info()    { echo -e "${CYAN}[INFO]${NC}  $*"; }
log_warn()    { echo -e "${YELLOW}[WARN]${NC}  $*"; }
log_error()   { echo -e "${RED}[ERROR]${NC} $*"; }
log_success() { echo -e "${GREEN}[OK]${NC}    $*"; }

# ============================================================================
# Usage
# ============================================================================
usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Automated PXE boot server setup. Installs and configures dnsmasq (DHCP + TFTP),
nginx (HTTP), generates PXE boot menus, Kickstart and Preseed templates.

Options:
  --interface <iface>       Network interface for DHCP/TFTP binding
                            (default: auto-detected from default route)
  --dhcp-range <start,end>  DHCP range as start,end
                            (default: 10.0.0.100,10.0.0.200)
  --tftp-root <path>        TFTP root directory
                            (default: /srv/tftp)
  --http-root <path>        HTTP document root for install media
                            (default: /var/www/pxe)
  --distros <list>          Comma-separated list of distros to configure
                            (default: rocky9)
  --help                    Print this help and exit

Supported distros:
  rocky9, rocky8, rhel9, rhel8, alma9,
  ubuntu2404, ubuntu2204, debian12, debian11

Examples:
  sudo $(basename "$0")
  sudo $(basename "$0") --interface eth0 --dhcp-range 10.0.0.100,10.0.0.200
  sudo $(basename "$0") --distros rocky9,ubuntu2404,debian12
  sudo $(basename "$0") --interface ens192 --tftp-root /data/tftp --distros rocky9

EOF
    exit 0
}

# ============================================================================
# Parse arguments
# ============================================================================
parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --interface)
                INTERFACE="$2"
                shift 2
                ;;
            --dhcp-range)
                DHCP_RANGE="$2"
                shift 2
                ;;
            --tftp-root)
                TFTP_ROOT="$2"
                shift 2
                ;;
            --http-root)
                HTTP_ROOT="$2"
                shift 2
                ;;
            --distros)
                DISTROS="$2"
                shift 2
                ;;
            --help)
                usage
                ;;
            *)
                log_error "Unknown option: $1"
                usage
                ;;
        esac
    done
}

# ============================================================================
# Detect OS
# ============================================================================
detect_os() {
    log_info "Detecting operating system..."

    if [[ ! -f /etc/os-release ]]; then
        log_error "Cannot detect OS — /etc/os-release not found"
        exit 1
    fi

    # shellcheck disable=SC1091
    source /etc/os-release

    OS_ID="${ID}"
    OS_VERSION="${VERSION_ID}"

    case "${OS_ID}" in
        debian|ubuntu)
            OS_FAMILY="debian"
            ;;
        rocky|rhel|almalinux|centos)
            OS_FAMILY="rhel"
            ;;
        *)
            log_error "Unsupported OS: ${OS_ID} ${OS_VERSION}"
            log_error "Supported: Debian 11+, Ubuntu 22.04+, RHEL/Rocky/Alma 8+"
            exit 1
            ;;
    esac

    log_success "Detected ${OS_ID} ${OS_VERSION} (${OS_FAMILY} family)"
}

# ============================================================================
# Detect network interface
# ============================================================================
detect_interface() {
    if [[ -n "${INTERFACE}" ]]; then
        if ! ip link show "${INTERFACE}" &>/dev/null; then
            log_error "Interface ${INTERFACE} does not exist"
            echo "Available interfaces:"
            ip -o link show | awk -F': ' '{print "  " $2}'
            exit 1
        fi
        log_info "Using specified interface: ${INTERFACE}"
    else
        log_info "Auto-detecting network interface..."
        INTERFACE=$(ip route show default 2>/dev/null | awk '{print $5; exit}')

        if [[ -z "${INTERFACE}" ]]; then
            log_error "Cannot detect default network interface"
            log_error "Specify one with --interface"
            exit 1
        fi
        log_success "Detected interface: ${INTERFACE}"
    fi

    SERVER_IP=$(ip -4 addr show "${INTERFACE}" | awk '/inet / {split($2,a,"/"); print a[1]; exit}')

    if [[ -z "${SERVER_IP}" ]]; then
        log_error "Cannot determine IP address for interface ${INTERFACE}"
        exit 1
    fi

    log_success "Server IP: ${SERVER_IP}"
}

# ============================================================================
# Validate distro list
# ============================================================================
validate_distros() {
    local valid_distros="rocky9 rocky8 rhel9 rhel8 alma9 ubuntu2404 ubuntu2204 debian12 debian11"

    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"

    for distro in "${DISTRO_LIST[@]}"; do
        local found=false
        for valid in ${valid_distros}; do
            if [[ "${distro}" == "${valid}" ]]; then
                found=true
                break
            fi
        done
        if [[ "${found}" == "false" ]]; then
            log_error "Invalid distro: ${distro}"
            log_error "Valid options: ${valid_distros}"
            exit 1
        fi
    done

    log_success "Distros to configure: ${DISTROS}"
}

# ============================================================================
# Install packages
# ============================================================================
install_packages() {
    log_info "Installing packages..."

    if [[ "${OS_FAMILY}" == "debian" ]]; then
        export DEBIAN_FRONTEND=noninteractive
        apt-get update -qq
        apt-get install -y -qq \
            dnsmasq \
            tftpd-hpa \
            syslinux-common \
            pxelinux \
            nginx \
            wget \
            curl \
            >/dev/null 2>&1

        if [[ $? -ne 0 ]]; then
            log_error "Package installation failed"
            exit 1
        fi
    elif [[ "${OS_FAMILY}" == "rhel" ]]; then
        dnf install -y -q \
            dnsmasq \
            tftp-server \
            syslinux \
            nginx \
            wget \
            curl \
            >/dev/null 2>&1

        if [[ $? -ne 0 ]]; then
            log_error "Package installation failed"
            exit 1
        fi
    fi

    log_success "Packages installed"
}

# ============================================================================
# Configure dnsmasq
# ============================================================================
configure_dnsmasq() {
    log_info "Configuring dnsmasq..."

    local dhcp_start dhcp_end
    dhcp_start=$(echo "${DHCP_RANGE}" | cut -d',' -f1)
    dhcp_end=$(echo "${DHCP_RANGE}" | cut -d',' -f2)

    # Disable default dnsmasq DNS to avoid conflicts
    if [[ -f /etc/dnsmasq.conf ]]; then
        cp /etc/dnsmasq.conf /etc/dnsmasq.conf.bak
    fi

    mkdir -p /etc/dnsmasq.d

    cat > /etc/dnsmasq.d/pxe.conf <<EOF
# PXE Boot Server Configuration
# Generated by install-pxe-server.sh

# Interface binding
interface=${INTERFACE}
bind-interfaces

# Disable DNS (use dnsmasq only for DHCP + TFTP)
port=0

# DHCP range — 1 hour lease
dhcp-range=${dhcp_start},${dhcp_end},255.255.255.0,1h

# PXE boot options — BIOS
dhcp-boot=pxelinux.0,,${SERVER_IP}

# UEFI detection — serve GRUB for UEFI clients
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-match=set:efi-x86_64,option:client-arch,9
dhcp-boot=tag:efi-x86_64,grubx64.efi,,${SERVER_IP}

# TFTP server
enable-tftp
tftp-root=${TFTP_ROOT}

# Set next-server for PXE
dhcp-option=66,${SERVER_IP}

# Logging
log-dhcp
log-queries
log-facility=/var/log/dnsmasq-pxe.log
EOF

    # Ensure main config includes the .d directory
    if [[ -f /etc/dnsmasq.conf ]]; then
        if ! grep -q "^conf-dir=/etc/dnsmasq.d" /etc/dnsmasq.conf; then
            echo "conf-dir=/etc/dnsmasq.d/,*.conf" >> /etc/dnsmasq.conf
        fi
    fi

    log_success "dnsmasq configured at /etc/dnsmasq.d/pxe.conf"
}

# ============================================================================
# Setup TFTP directory
# ============================================================================
setup_tftp() {
    log_info "Setting up TFTP directory..."

    mkdir -p "${TFTP_ROOT}/pxelinux.cfg"
    mkdir -p "${TFTP_ROOT}/grub"

    # Copy syslinux/pxelinux files
    if [[ "${OS_FAMILY}" == "debian" ]]; then
        local pxe_src="/usr/lib/PXELINUX"
        local sys_src="/usr/lib/syslinux/modules/bios"

        if [[ -f "${pxe_src}/pxelinux.0" ]]; then
            cp "${pxe_src}/pxelinux.0" "${TFTP_ROOT}/"
        else
            log_warn "pxelinux.0 not found at ${pxe_src}/pxelinux.0"
        fi

        for file in ldlinux.c32 menu.c32 libmenu.c32 libutil.c32; do
            if [[ -f "${sys_src}/${file}" ]]; then
                cp "${sys_src}/${file}" "${TFTP_ROOT}/"
            else
                log_warn "${file} not found at ${sys_src}/${file}"
            fi
        done

    elif [[ "${OS_FAMILY}" == "rhel" ]]; then
        local sys_src="/usr/share/syslinux"

        for file in pxelinux.0 ldlinux.c32 menu.c32 libmenu.c32 libutil.c32; do
            if [[ -f "${sys_src}/${file}" ]]; then
                cp "${sys_src}/${file}" "${TFTP_ROOT}/"
            else
                log_warn "${file} not found at ${sys_src}/${file}"
            fi
        done
    fi

    # Create distro directories in TFTP root
    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"
    for distro in "${DISTRO_LIST[@]}"; do
        mkdir -p "${TFTP_ROOT}/${distro}"
    done

    # Set permissions
    chmod -R 755 "${TFTP_ROOT}"

    if [[ "${OS_FAMILY}" == "debian" ]]; then
        chown -R tftp:tftp "${TFTP_ROOT}"

        # Configure tftpd-hpa
        cat > /etc/default/tftpd-hpa <<EOF
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="${TFTP_ROOT}"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--secure --verbose"
EOF
    elif [[ "${OS_FAMILY}" == "rhel" ]]; then
        chown -R nobody:nobody "${TFTP_ROOT}"
    fi

    log_success "TFTP directory structure created at ${TFTP_ROOT}"
}

# ============================================================================
# Create PXE boot menu
# ============================================================================
create_pxe_menu() {
    log_info "Generating PXE boot menu..."

    local menu_file="${TFTP_ROOT}/pxelinux.cfg/default"

    cat > "${menu_file}" <<'MENU_HEADER'
DEFAULT menu.c32
PROMPT 0
TIMEOUT 300
ONTIMEOUT local

MENU TITLE ===== PXE Boot Server =====
MENU COLOR border       30;44   #40ffffff #a0000000 std
MENU COLOR title        1;36;44 #9033cccc #a0000000 std
MENU COLOR sel          7;37;40 #e0ffffff #20ffffff all
MENU COLOR unsel        37;44   #50ffffff #a0000000 std
MENU COLOR help         37;40   #c0ffffff #a0000000 std
MENU COLOR timeout_msg  37;40   #80ffffff #00000000 std
MENU COLOR timeout      1;37;40 #c0ffffff #00000000 std

LABEL local
    MENU LABEL Boot from local disk
    MENU DEFAULT
    LOCALBOOT 0

MENU_HEADER

    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"
    for distro in "${DISTRO_LIST[@]}"; do
        case "${distro}" in
            rocky9)
                cat >> "${menu_file}" <<EOF

LABEL rocky9
    MENU LABEL Install Rocky Linux 9
    KERNEL rocky9/vmlinuz
    APPEND initrd=rocky9/initrd.img inst.ks=http://${SERVER_IP}/ks/ks-rocky9.cfg ip=dhcp
EOF
                ;;
            rocky8)
                cat >> "${menu_file}" <<EOF

LABEL rocky8
    MENU LABEL Install Rocky Linux 8
    KERNEL rocky8/vmlinuz
    APPEND initrd=rocky8/initrd.img inst.ks=http://${SERVER_IP}/ks/ks-rocky8.cfg ip=dhcp
EOF
                ;;
            rhel9)
                cat >> "${menu_file}" <<EOF

LABEL rhel9
    MENU LABEL Install RHEL 9
    KERNEL rhel9/vmlinuz
    APPEND initrd=rhel9/initrd.img inst.ks=http://${SERVER_IP}/ks/ks-rhel9.cfg ip=dhcp
EOF
                ;;
            rhel8)
                cat >> "${menu_file}" <<EOF

LABEL rhel8
    MENU LABEL Install RHEL 8
    KERNEL rhel8/vmlinuz
    APPEND initrd=rhel8/initrd.img inst.ks=http://${SERVER_IP}/ks/ks-rhel8.cfg ip=dhcp
EOF
                ;;
            alma9)
                cat >> "${menu_file}" <<EOF

LABEL alma9
    MENU LABEL Install AlmaLinux 9
    KERNEL alma9/vmlinuz
    APPEND initrd=alma9/initrd.img inst.ks=http://${SERVER_IP}/ks/ks-alma9.cfg ip=dhcp
EOF
                ;;
            ubuntu2404)
                cat >> "${menu_file}" <<EOF

LABEL ubuntu2404
    MENU LABEL Install Ubuntu 24.04 LTS
    KERNEL ubuntu2404/linux
    APPEND initrd=ubuntu2404/initrd.gz auto=true priority=critical url=http://${SERVER_IP}/preseed/preseed-ubuntu2404.cfg interface=auto
EOF
                ;;
            ubuntu2204)
                cat >> "${menu_file}" <<EOF

LABEL ubuntu2204
    MENU LABEL Install Ubuntu 22.04 LTS
    KERNEL ubuntu2204/linux
    APPEND initrd=ubuntu2204/initrd.gz auto=true priority=critical url=http://${SERVER_IP}/preseed/preseed-ubuntu2204.cfg interface=auto
EOF
                ;;
            debian12)
                cat >> "${menu_file}" <<EOF

LABEL debian12
    MENU LABEL Install Debian 12 (Bookworm)
    KERNEL debian12/linux
    APPEND initrd=debian12/initrd.gz auto=true priority=critical url=http://${SERVER_IP}/preseed/preseed-debian12.cfg interface=auto
EOF
                ;;
            debian11)
                cat >> "${menu_file}" <<EOF

LABEL debian11
    MENU LABEL Install Debian 11 (Bullseye)
    KERNEL debian11/linux
    APPEND initrd=debian11/initrd.gz auto=true priority=critical url=http://${SERVER_IP}/preseed/preseed-debian11.cfg interface=auto
EOF
                ;;
        esac
    done

    log_success "PXE boot menu created at ${menu_file}"
}

# ============================================================================
# Generate Kickstart template
# ============================================================================
generate_kickstart() {
    local distro="$1"
    local ks_dir="${HTTP_ROOT}/ks"
    local ks_file="${ks_dir}/ks-${distro}.cfg"

    mkdir -p "${ks_dir}"

    log_info "Generating Kickstart template: ${ks_file}"

    cat > "${ks_file}" <<EOF
# Kickstart configuration for ${distro}
# Generated by install-pxe-server.sh
# Edit this file to match your environment

# Install method — update URL to match your repo path
url --url="http://${SERVER_IP}/${distro}/"
text
reboot

# Locale
lang en_US.UTF-8
keyboard us
timezone UTC --utc

# Network
network --bootproto=dhcp --device=link --activate --onboot=yes
network --hostname=${distro}-host

# Authentication
# Generate hash: python3 -c "import crypt; print(crypt.crypt('password', crypt.mksalt(crypt.METHOD_SHA512)))"
rootpw --iscrypted \$6\$rounds=4096\$CHANGEME\$CHANGEME_HASH_HERE

# Create admin user
user --name=admin --groups=wheel --iscrypted --password=\$6\$rounds=4096\$CHANGEME\$CHANGEME_HASH_HERE

# Security
selinux --enforcing
firewall --enabled --service=ssh

# Disk partitioning — auto with LVM
ignoredisk --only-use=sda
clearpart --all --initlabel --drives=sda
autopart --type=lvm --fstype=xfs

# Bootloader
bootloader --append="crashkernel=auto" --location=mbr --boot-drive=sda

# Package selection
%packages
@^minimal-environment
@standard
bash-completion
vim-enhanced
tmux
curl
wget
net-tools
bind-utils
chrony
rsync
%end

# Post-install script
%post --log=/root/ks-post.log
#!/bin/bash

# Enable SSH
systemctl enable --now sshd

# Update packages
dnf update -y

# Configure chrony
systemctl enable --now chronyd

# Set up automatic security updates
dnf install -y dnf-automatic
sed -i 's/apply_updates = no/apply_updates = yes/' /etc/dnf/automatic.conf
systemctl enable --now dnf-automatic-install.timer

# Disable root SSH login
sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config

echo "Post-install completed: \$(date)" >> /root/ks-post.log
%end
EOF

    log_success "Kickstart template created: ${ks_file}"
}

# ============================================================================
# Generate Preseed template
# ============================================================================
generate_preseed() {
    local distro="$1"
    local preseed_dir="${HTTP_ROOT}/preseed"
    local preseed_file="${preseed_dir}/preseed-${distro}.cfg"

    mkdir -p "${preseed_dir}"

    log_info "Generating Preseed template: ${preseed_file}"

    local mirror_host="deb.debian.org"
    local mirror_dir="/debian"
    if [[ "${distro}" == ubuntu* ]]; then
        mirror_host="archive.ubuntu.com"
        mirror_dir="/ubuntu"
    fi

    cat > "${preseed_file}" <<EOF
# Preseed configuration for ${distro}
# Generated by install-pxe-server.sh
# Edit this file to match your environment

### Locale and keyboard
d-i debian-installer/locale string en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select us
d-i console-setup/ask_detect boolean false

### Network
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string ${distro}-host
d-i netcfg/get_domain string lab.local
d-i netcfg/hostname string ${distro}-host

### Mirror
d-i mirror/country string manual
d-i mirror/http/hostname string ${mirror_host}
d-i mirror/http/directory string ${mirror_dir}
d-i mirror/http/proxy string

### Clock and timezone
d-i clock-setup/utc boolean true
d-i time/zone string UTC
d-i clock-setup/ntp boolean true

### Partitioning — guided LVM entire disk
d-i partman-auto/method string lvm
d-i partman-auto-lvm/guided_size string max
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
d-i partman-lvm/confirm boolean true
d-i partman-lvm/confirm_nooverwrite boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true
d-i partman/confirm_nooverwrite boolean true

### User account
# Generate hash: mkpasswd -m sha-512 'password'
d-i passwd/root-login boolean false
d-i passwd/user-fullname string Admin User
d-i passwd/username string admin
d-i passwd/user-password-crypted password \$6\$rounds=4096\$CHANGEME\$CHANGEME_HASH_HERE

### Package selection
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/include string bash-completion vim tmux curl wget net-tools rsync
d-i pkgsel/upgrade select full-upgrade
popularity-contest popularity-contest/participate boolean false

### GRUB bootloader
d-i grub-installer/only_debian boolean true
d-i grub-installer/bootdev string default

### Post-install commands
d-i preseed/late_command string \\
    in-target systemctl enable ssh ; \\
    echo "Post-install completed: \$(date)" >> /target/root/preseed-post.log

### Reboot after install
d-i finish-install/reboot_in_progress note
d-i debian-installer/exit/poweroff boolean false
EOF

    log_success "Preseed template created: ${preseed_file}"
}

# ============================================================================
# Configure nginx
# ============================================================================
configure_nginx() {
    log_info "Configuring nginx..."

    mkdir -p "${HTTP_ROOT}"/{ks,preseed}

    # Create distro directories in HTTP root
    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"
    for distro in "${DISTRO_LIST[@]}"; do
        mkdir -p "${HTTP_ROOT}/${distro}"
    done

    # Remove default site if present
    rm -f /etc/nginx/sites-enabled/default 2>/dev/null
    rm -f /etc/nginx/conf.d/default.conf 2>/dev/null

    cat > /etc/nginx/conf.d/pxe.conf <<EOF
# PXE Boot HTTP Server
# Generated by install-pxe-server.sh

server {
    listen 80;
    server_name _;

    root ${HTTP_ROOT};

    location / {
        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
    }

    location /ks/ {
        alias ${HTTP_ROOT}/ks/;
        autoindex on;
    }

    location /preseed/ {
        alias ${HTTP_ROOT}/preseed/;
        autoindex on;
    }
}
EOF

    # Test nginx config
    if nginx -t &>/dev/null; then
        log_success "nginx configured at /etc/nginx/conf.d/pxe.conf"
    else
        log_error "nginx configuration test failed"
        nginx -t
        exit 1
    fi
}

# ============================================================================
# Configure firewall
# ============================================================================
configure_firewall() {
    log_info "Configuring firewall..."

    if command -v firewall-cmd &>/dev/null && systemctl is-active --quiet firewalld; then
        log_info "Detected firewalld"
        firewall-cmd --permanent --add-port=67/udp   # DHCP
        firewall-cmd --permanent --add-port=68/udp   # DHCP client
        firewall-cmd --permanent --add-port=69/udp   # TFTP
        firewall-cmd --permanent --add-port=80/tcp   # HTTP
        firewall-cmd --permanent --add-port=4011/udp # ProxyDHCP
        firewall-cmd --reload
        log_success "Firewall ports opened (firewalld)"

    elif command -v ufw &>/dev/null && ufw status | grep -q "Status: active"; then
        log_info "Detected ufw"
        ufw allow 67/udp comment "DHCP server"
        ufw allow 68/udp comment "DHCP client"
        ufw allow 69/udp comment "TFTP"
        ufw allow 80/tcp comment "HTTP"
        ufw allow 4011/udp comment "ProxyDHCP"
        log_success "Firewall ports opened (ufw)"

    else
        log_warn "No active firewall detected — skipping firewall configuration"
        log_warn "Manually open ports: 67/udp, 68/udp, 69/udp, 80/tcp, 4011/udp"
    fi
}

# ============================================================================
# Enable services
# ============================================================================
enable_services() {
    log_info "Enabling and starting services..."

    # dnsmasq
    systemctl enable dnsmasq
    systemctl restart dnsmasq
    if systemctl is-active --quiet dnsmasq; then
        log_success "dnsmasq is running"
    else
        log_error "dnsmasq failed to start"
        journalctl -u dnsmasq --no-pager -n 10
    fi

    # TFTP
    if [[ "${OS_FAMILY}" == "debian" ]]; then
        systemctl enable tftpd-hpa
        systemctl restart tftpd-hpa
        if systemctl is-active --quiet tftpd-hpa; then
            log_success "tftpd-hpa is running"
        else
            log_error "tftpd-hpa failed to start"
            journalctl -u tftpd-hpa --no-pager -n 10
        fi
    elif [[ "${OS_FAMILY}" == "rhel" ]]; then
        systemctl enable tftp.socket
        systemctl restart tftp.socket
        if systemctl is-active --quiet tftp.socket; then
            log_success "tftp.socket is running"
        else
            log_error "tftp.socket failed to start"
            journalctl -u tftp.socket --no-pager -n 10
        fi
    fi

    # nginx
    systemctl enable nginx
    systemctl restart nginx
    if systemctl is-active --quiet nginx; then
        log_success "nginx is running"
    else
        log_error "nginx failed to start"
        journalctl -u nginx --no-pager -n 10
    fi
}

# ============================================================================
# Print summary
# ============================================================================
print_summary() {
    local dhcp_start dhcp_end
    dhcp_start=$(echo "${DHCP_RANGE}" | cut -d',' -f1)
    dhcp_end=$(echo "${DHCP_RANGE}" | cut -d',' -f2)

    echo ""
    echo "╔══════════════════════════════════════════════════════════════╗"
    echo "║               PXE Boot Server — Setup Complete             ║"
    echo "╠══════════════════════════════════════════════════════════════╣"
    echo "║                                                            ║"
    printf "║  Server IP:      %-41s║\n" "${SERVER_IP}"
    printf "║  Interface:      %-41s║\n" "${INTERFACE}"
    printf "║  DHCP Range:     %-41s║\n" "${dhcp_start} — ${dhcp_end}"
    printf "║  TFTP Root:      %-41s║\n" "${TFTP_ROOT}"
    printf "║  HTTP Root:      %-41s║\n" "${HTTP_ROOT}"
    printf "║  Distros:        %-41s║\n" "${DISTROS}"
    echo "║                                                            ║"
    echo "╠══════════════════════════════════════════════════════════════╣"
    echo "║  Configuration Files                                       ║"
    echo "╠══════════════════════════════════════════════════════════════╣"
    printf "║  dnsmasq:        %-41s║\n" "/etc/dnsmasq.d/pxe.conf"
    printf "║  PXE menu:       %-41s║\n" "${TFTP_ROOT}/pxelinux.cfg/default"
    printf "║  nginx:          %-41s║\n" "/etc/nginx/conf.d/pxe.conf"
    echo "║                                                            ║"

    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"
    for distro in "${DISTRO_LIST[@]}"; do
        case "${distro}" in
            rocky*|rhel*|alma*)
                printf "║  Kickstart:      %-41s║\n" "http://${SERVER_IP}/ks/ks-${distro}.cfg"
                ;;
            ubuntu*|debian*)
                printf "║  Preseed:        %-41s║\n" "http://${SERVER_IP}/preseed/preseed-${distro}.cfg"
                ;;
        esac
    done

    echo "║                                                            ║"
    echo "╠══════════════════════════════════════════════════════════════╣"
    echo "║  Next Steps                                                ║"
    echo "╠══════════════════════════════════════════════════════════════╣"
    echo "║                                                            ║"
    echo "║  1. Mount or extract distro ISOs into the HTTP root:       ║"

    for distro in "${DISTRO_LIST[@]}"; do
        printf "║     mount -o loop,ro <iso> %-33s║\n" "${HTTP_ROOT}/${distro}/"
    done

    echo "║                                                            ║"
    echo "║  2. Copy kernel + initrd to TFTP root:                     ║"

    for distro in "${DISTRO_LIST[@]}"; do
        printf "║     %s -> %-43s║\n" "vmlinuz/initrd" "${TFTP_ROOT}/${distro}/"
    done

    echo "║                                                            ║"
    echo "║  3. Edit Kickstart/Preseed templates:                      ║"
    echo "║     - Set root/user password hashes                        ║"
    echo "║     - Adjust partitioning layout                           ║"
    echo "║     - Add site-specific packages                           ║"
    echo "║                                                            ║"
    echo "║  4. PXE boot a target machine and select a distro          ║"
    echo "║                                                            ║"
    echo "╚══════════════════════════════════════════════════════════════╝"
    echo ""
}

# ============================================================================
# Main
# ============================================================================
main() {
    echo ""
    echo "================================================"
    echo "  PXE Boot Server — Automated Setup"
    echo "  Version 1.0.0"
    echo "================================================"
    echo ""

    # Check root
    if [[ "${EUID}" -ne 0 ]]; then
        log_error "This script must be run as root"
        exit 1
    fi

    parse_args "$@"
    detect_os
    detect_interface
    validate_distros
    install_packages
    configure_dnsmasq
    setup_tftp
    create_pxe_menu

    # Generate answer file templates based on selected distros
    IFS=',' read -ra DISTRO_LIST <<< "${DISTROS}"
    for distro in "${DISTRO_LIST[@]}"; do
        case "${distro}" in
            rocky*|rhel*|alma*)
                generate_kickstart "${distro}"
                ;;
            ubuntu*|debian*)
                generate_preseed "${distro}"
                ;;
        esac
    done

    configure_nginx
    configure_firewall
    enable_services
    print_summary

    log_success "PXE boot server setup complete"
}

main "$@"
