#!/usr/bin/env bash

# NOTE: set -e is intentionally omitted because this is a diagnostic script
# that must continue running even when individual checks fail.
set -u
umask 022

LOGFILE="/var/log/check.log"

TINET_SOURCE="/mnt/c/tinet/tinet"
TINET_DEST="/usr/bin/tinet"

RESOLV_EXPECTED='nameserver 8.8.8.8
nameserver 1.1.1.1'

WSL_CONF_EXPECTED='[network]
hostname = UBUNTU
generateResolvConf = false
[boot]
systemd = true
command = systemctl start docker
[user]
default = root'

PASS_COUNT=0
WARN_COUNT=0
FAIL_COUNT=0
FAIL_REASONS=()

###############################################################################
# Logging
###############################################################################

log() {
    printf '%s\n' "$1" | tee -a "$LOGFILE"
}

log_only() {
    printf '%s\n' "$1" >> "$LOGFILE"
}

section() {
    printf '\n========================================================\n' | tee -a "$LOGFILE"
    printf '%s\n' "$1" | tee -a "$LOGFILE"
    printf '========================================================\n' | tee -a "$LOGFILE"
}

pass() {
    PASS_COUNT=$((PASS_COUNT + 1))
    log "[OK]   $1"
}

warn() {
    WARN_COUNT=$((WARN_COUNT + 1))
    log "[WARN] $1"
}

fail_check() {
    FAIL_COUNT=$((FAIL_COUNT + 1))
    FAIL_REASONS+=("$1")
    log "[FAIL] $1"
}

info() {
    log "[INFO] $1"
}

###############################################################################
# Helpers
###############################################################################

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

is_wsl() {
    grep -qi microsoft /proc/version 2>/dev/null
}

is_immutable() {
    local file="$1"
    lsattr "$file" 2>/dev/null | awk '{print $1}' | grep -q 'i'
}

normalize_file_for_compare() {
    local file="$1"
    # Remove only the final blank line so that trailing newline differences
    # do not cause false mismatches.
    sed -e '${/^$/d;}' "$file"
}

normalize_string_for_compare() {
    printf '%s\n' "$1"
}

file_matches_expected() {
    local file="$1"
    local expected="$2"

    [ -f "$file" ] || return 1

    diff -u \
        <(normalize_file_for_compare "$file") \
        <(normalize_string_for_compare "$expected") \
        >/dev/null 2>&1
}

###############################################################################
# Diagnostics
###############################################################################

check_environment() {
    section "Environment"

    if is_wsl; then
        pass "This environment appears to be running under WSL."
    else
        warn "This environment does not appear to be WSL."
    fi

    if [ -f /proc/version ]; then
        log_only ""
        log_only "----- /proc/version -----"
        cat /proc/version >> "$LOGFILE"
        log_only "-------------------------"
    fi

    if [ -f /etc/os-release ]; then
        log_only ""
        log_only "----- /etc/os-release -----"
        cat /etc/os-release >> "$LOGFILE"
        log_only "---------------------------"
    fi

    info "Current user: $(id -un)"
    info "Hostname: $(hostname)"
    info "Current shell: ${SHELL:-unknown}"
}

check_windows_side() {
    section "Windows-side WSL status"

    if [ -x /mnt/c/Windows/System32/wsl.exe ]; then
        pass "wsl.exe is accessible from this WSL instance."

        log_only ""
        log_only "----- wsl.exe -l -v -----"
        if /mnt/c/Windows/System32/wsl.exe -l -v 2>&1 | tr -d '\0' >> "$LOGFILE"; then
            pass "Successfully collected 'wsl.exe -l -v'."
        else
            warn "wsl.exe exists but 'wsl.exe -l -v' failed."
        fi
        log_only "-------------------------"
    else
        warn "wsl.exe is not accessible at /mnt/c/Windows/System32/wsl.exe."
    fi
}

check_resolv_conf() {
    section "/etc/resolv.conf"

    if [ -e /etc/resolv.conf ] || [ -L /etc/resolv.conf ]; then
        pass "/etc/resolv.conf exists."
    else
        fail_check "/etc/resolv.conf does not exist."
        return
    fi

    if [ -L /etc/resolv.conf ]; then
        fail_check "/etc/resolv.conf is a symbolic link."
        info "Link target: $(readlink /etc/resolv.conf || true)"
    else
        pass "/etc/resolv.conf is a regular file."
    fi

    log_only ""
    log_only "----- /etc/resolv.conf -----"
    cat /etc/resolv.conf >> "$LOGFILE"
    log_only "----------------------------"

    if command_exists lsattr; then
        log_only ""
        log_only "----- lsattr /etc/resolv.conf -----"
        lsattr /etc/resolv.conf >> "$LOGFILE" 2>&1
        log_only "-----------------------------------"
    else
        warn "lsattr command is not available."
    fi

    if file_matches_expected /etc/resolv.conf "$RESOLV_EXPECTED"; then
        pass "/etc/resolv.conf content matches the expected configuration."
    else
        fail_check "/etc/resolv.conf content does not match the expected configuration."
        log_only ""
        log_only "----- expected /etc/resolv.conf -----"
        printf '%s\n' "$RESOLV_EXPECTED" >> "$LOGFILE"
        log_only "-------------------------------------"
        log_only ""
        log_only "----- diff -----"
        diff -u \
            <(normalize_string_for_compare "$RESOLV_EXPECTED") \
            <(normalize_file_for_compare /etc/resolv.conf) \
            >> "$LOGFILE" 2>&1 || true
        log_only "----------------"
    fi

    if is_immutable /etc/resolv.conf; then
        pass "/etc/resolv.conf has the immutable flag."
    else
        fail_check "/etc/resolv.conf does not have the immutable flag."
    fi
}

check_wsl_conf() {
    section "/etc/wsl.conf"

    if [ -f /etc/wsl.conf ]; then
        pass "/etc/wsl.conf exists."
    else
        fail_check "/etc/wsl.conf does not exist."
        return
    fi

    log_only ""
    log_only "----- /etc/wsl.conf -----"
    cat /etc/wsl.conf >> "$LOGFILE"
    log_only "-------------------------"

    if file_matches_expected /etc/wsl.conf "$WSL_CONF_EXPECTED"; then
        pass "/etc/wsl.conf matches the expected configuration."
    else
        fail_check "/etc/wsl.conf does not match the expected configuration."
        log_only ""
        log_only "----- expected /etc/wsl.conf -----"
        printf '%s\n' "$WSL_CONF_EXPECTED" >> "$LOGFILE"
        log_only "----------------------------------"
        log_only ""
        log_only "----- diff -----"
        diff -u \
            <(normalize_string_for_compare "$WSL_CONF_EXPECTED") \
            <(normalize_file_for_compare /etc/wsl.conf) \
            >> "$LOGFILE" 2>&1 || true
        log_only "----------------"
    fi
}

check_boot_settings() {
    section "Boot and systemd"

    if [ -d /run/systemd/system ]; then
        pass "systemd appears to be active."
    else
        fail_check "systemd does not appear to be active."
    fi

    if command_exists systemctl; then
        pass "'systemctl' command is available."
    else
        fail_check "'systemctl' command is not available."
    fi
}

check_network() {
    section "Network"

    if command_exists ip; then
        pass "'ip' command is available."

        log_only ""
        log_only "----- ip addr -----"
        ip addr >> "$LOGFILE" 2>&1
        log_only "-------------------"

        log_only ""
        log_only "----- ip route -----"
        ip route >> "$LOGFILE" 2>&1
        log_only "--------------------"
    else
        fail_check "'ip' command is not available."
    fi

    if ping -c 2 8.8.8.8 >> "$LOGFILE" 2>&1; then
        pass "Outbound connectivity to 8.8.8.8 is working."
    else
        fail_check "Outbound connectivity to 8.8.8.8 failed."
    fi

    log_only ""
    log_only "----- resolvectl dns -----"
    resolvectl dns >> "$LOGFILE" 2>&1
    log_only "--------------------------"

    if resolvectl query google.com >> "$LOGFILE" 2>&1; then
        pass "DNS resolution is working (google.com)."
    else
        fail_check "DNS resolution failed (google.com)."
    fi
}

check_docker() {
    section "Docker"

    if dpkg -s docker.io >/dev/null 2>&1; then
        pass "docker.io package is installed."
    elif dpkg -s docker-ce >/dev/null 2>&1; then
        warn "docker-ce package is installed (expected docker.io)."
    else
        fail_check "No Docker package is installed (docker.io or docker-ce)."
    fi

    if command_exists docker; then
        pass "'docker' command is available."
        info "Docker version: $(docker --version 2>/dev/null || echo unknown)"
    else
        fail_check "'docker' command is not available."
    fi

    if ! command_exists systemctl; then
        fail_check "'systemctl' command is not available."
        return
    fi

    pass "'systemctl' command is available for Docker checks."

    if systemctl is-enabled docker >/dev/null 2>&1; then
        pass "docker.service is enabled."
    else
        fail_check "docker.service is not enabled."
    fi

    local docker_active=false
    if systemctl is-active docker >/dev/null 2>&1; then
        docker_active=true
        pass "docker.service is running."
    else
        fail_check "docker.service is not running."
    fi

    if systemctl is-enabled docker.socket >/dev/null 2>&1; then
        pass "docker.socket is enabled."
    else
        warn "docker.socket is not enabled."
    fi

    # If docker.socket is active while docker.service is running, that is normal.
    # If only the socket is active and the service is stopped, report it as a warning.
    if systemctl is-active docker.socket >/dev/null 2>&1; then
        if [ "$docker_active" = true ]; then
            pass "docker.socket is active."
        else
            warn "docker.socket is active but docker.service is not running (socket-only state)."
        fi
    else
        pass "docker.socket is not active."
    fi

    log_only ""
    log_only "----- systemctl status docker --no-pager -----"
    systemctl status docker --no-pager >> "$LOGFILE" 2>&1 || true
    log_only "----------------------------------------------"

    log_only ""
    log_only "----- systemctl status docker.socket --no-pager -----"
    systemctl status docker.socket --no-pager >> "$LOGFILE" 2>&1 || true
    log_only "-----------------------------------------------------"
}

check_tinet() {
    section "tinet"

    if [ -x "$TINET_DEST" ]; then
        pass "tinet is installed at $TINET_DEST."
    else
        fail_check "tinet is not installed at $TINET_DEST."
    fi

    if [ -f "$TINET_SOURCE" ]; then
        pass "Source tinet file exists at $TINET_SOURCE."
    else
        warn "Source tinet file does not exist at $TINET_SOURCE."
    fi
}

print_summary() {
    section "Summary"

    log "Passed  : $PASS_COUNT"
    log "Warnings: $WARN_COUNT"
    log "Failed  : $FAIL_COUNT"
    log ""
    log "Diagnostic log file: $LOGFILE"

    if [ "$FAIL_COUNT" -eq 0 ]; then
        log ""
        if [ "$WARN_COUNT" -eq 0 ]; then
            log "Overall result: ALL CHECKS PASSED"
        else
            log "Overall result: ALL CHECKS PASSED (with warnings)"
        fi
    else
        log ""
        log "Overall result: SOME CHECKS FAILED"
        log ""
        log "Failed checks:"
        if [ "${#FAIL_REASONS[@]}" -gt 0 ]; then
            local reason
            for reason in "${FAIL_REASONS[@]}"; do
                log "  - $reason"
            done
        fi
    fi
}

###############################################################################
# Main
###############################################################################

main() {
    if [ "$(id -u)" -ne 0 ]; then
        echo "ERROR: This script must be run as root." >&2
        exit 1
    fi

    printf '\n'
    read -r -p "Have you restarted your WSL2 instance after running setup.sh? [y/N] " answer
    case "$answer" in
        [yY]|[yY][eE][sS]) ;;
        *)
            echo "Please restart your WSL2 instance first."
            exit 0
            ;;
    esac

    : > "$LOGFILE"

    section "Starting diagnostic"

    check_environment
    check_windows_side
    check_resolv_conf
    check_wsl_conf
    check_boot_settings
    check_network
    check_docker
    check_tinet
    print_summary
}

main "$@"
