#!/bin/bash
# JellyShip TTY1 — Compact Status Dashboard + Background Ansible
# Fits 24 lines. No flicker (pre-render + swap). Ansible in background.
# Retry ansible up to 5x until changed=0 and failed=0.

[ -f /var/lib/dpkg/info/base-installer.list ] && exit 0
[ -d /cdrom/pool ] && [ ! -f /etc/jellyship/.preseed-complete ] && exit 0

export TERM=linux
export HOME=/root
exec </dev/tty1 >/dev/tty1 2>&1

# ── Colors ──
R="\033[0m"; W="\033[1;37m"; G="\033[1;32m"; Y="\033[1;33m"
C="\033[1;36m"; DIM="\033[0;37m"; RED="\033[1;31m"

LOG_DIR="/var/log/jellyship"
PLAYBOOK="/opt/jellyship/ansible/site.yml"
TASK_FILE="/tmp/jellyship-ansible-task"
RESULT_FILE="/tmp/jellyship-ansible-result"
NET_CACHE="/tmp/jellyship-net-cache"
mkdir -p "$LOG_DIR"

# ══════════════════════════════════════════════════════════════════════
# BOOTSTRAP (blocking)
# ══════════════════════════════════════════════════════════════════════
if ! command -v ansible-playbook >/dev/null 2>&1 || ! command -v node >/dev/null 2>&1; then
    clear
    echo ""
    echo -e "  ${C}    ╦╔═╗╦  ╦  ╦ ╦╔═╗╦ ╦╦╔═╗${R}"
    echo -e "  ${C}    ║║╣ ║  ║  ╚╦╝╚═╗╠═╣║╠═╝${R}"
    echo -e "  ${C}   ╚╝╚═╝╩═╝╩═╝ ╩ ╚═╝╩ ╩╩╩  ${R}"
    echo ""
    echo -e "  ${Y}▸${R} Waiting for network..."
    for i in $(seq 1 60); do
        curl -sf --connect-timeout 3 https://deb.debian.org/ >/dev/null 2>&1 && break
        sleep 1
    done
    if ! command -v ansible-playbook >/dev/null 2>&1; then
        echo -e "  ${Y}▸${R} Installing Ansible..."
        apt-get update -qq >/dev/null 2>&1
        apt-get install -y ansible >/dev/null 2>&1
        ansible-galaxy collection install community.general >/dev/null 2>&1
    fi
    if ! command -v node >/dev/null 2>&1; then
        echo -e "  ${Y}▸${R} Installing Node.js 22..."
        curl -fsSL https://deb.nodesource.com/setup_22.x 2>/dev/null | bash - >/dev/null 2>&1
        apt-get install -y nodejs >/dev/null 2>&1
    fi
    echo -e "  ${G}✓${R} Bootstrap complete"
    sleep 2
fi

# ══════════════════════════════════════════════════════════════════════
# NETWORK INFO CACHE (refreshed every 5 minutes)
# ══════════════════════════════════════════════════════════════════════
refresh_net_cache() {
    (
        # Local IPs
        LAN4=$(ip -4 addr show scope global 2>/dev/null | grep -oP 'inet \K[0-9.]+' | head -1)
        LAN6=$(ip -6 addr show scope global 2>/dev/null | grep -oP 'inet6 \K[0-9a-f:]+' | grep -v '^fe80' | head -1)

        # WAN IPs + ISP org via ipapi.co
        WAN4_JSON=$(curl -4 -sf --connect-timeout 5 https://ipapi.co/json/ 2>/dev/null)
        WAN6_JSON=$(curl -6 -sf --connect-timeout 5 https://ipapi.co/json/ 2>/dev/null)

        WAN4=$(echo "$WAN4_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('ip',''))" 2>/dev/null)
        ORG4=$(echo "$WAN4_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('org',''))" 2>/dev/null)
        WAN6=$(echo "$WAN6_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('ip',''))" 2>/dev/null)
        ORG6=$(echo "$WAN6_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('org',''))" 2>/dev/null)

        # Use same org if both match
        [ -z "$ORG4" ] && ORG4="$ORG6"
        [ -z "$ORG6" ] && ORG6="$ORG4"

        cat > "$NET_CACHE" << CACHEEOF
LAN4="$LAN4"
LAN6="$LAN6"
WAN4="$WAN4"
WAN6="$WAN6"
ORG4="$ORG4"
ORG6="$ORG6"
CACHEEOF
    ) &
}

load_net_cache() {
    LAN4=""; LAN6=""; WAN4=""; WAN6=""; ORG4=""; ORG6=""
    [ -f "$NET_CACHE" ] && source "$NET_CACHE"
}

# ══════════════════════════════════════════════════════════════════════
# ANSIBLE BACKGROUND RUNNER (retry up to 5x until clean)
# ══════════════════════════════════════════════════════════════════════
run_ansible_bg() {
    [ ! -f "$PLAYBOOK" ] && return
    [ -f /tmp/jellyship-ansible.pid ] && kill -0 $(cat /tmp/jellyship-ansible.pid) 2>/dev/null && return

    (
        echo "running" > "$RESULT_FILE"
        echo "Starting..." > "$TASK_FILE"

        for ATTEMPT in 1 2 3 4 5; do
            LOGFILE="$LOG_DIR/ansible-$(date +%Y%m%d-%H%M%S).log"

            ANSIBLE_FORCE_COLOR=0 ANSIBLE_STDOUT_CALLBACK=default \
            ansible-playbook "$PLAYBOOK" \
                --connection=local \
                -i "localhost," \
                2>&1 | tee "$LOGFILE" | while IFS= read -r line; do
                case "$line" in
                    "TASK ["*"]"*)
                        echo "$line" | sed 's/TASK \[//;s/\] \*.*$//' > "$TASK_FILE"
                        ;;
                esac
            done

            ln -sf "$LOGFILE" "$LOG_DIR/ansible-latest.log"

            RECAP=$(grep -A1 "PLAY RECAP" "$LOGFILE" 2>/dev/null | tail -1)
            CH=$(echo "$RECAP" | grep -oP 'changed=\K\d+')
            FA=$(echo "$RECAP" | grep -oP 'failed=\K\d+')
            OK=$(echo "$RECAP" | grep -oP 'ok=\K\d+')

            if [ "${FA:-1}" = "0" ] && [ "${CH:-1}" = "0" ]; then
                echo "ok=${OK:-0} changed=0 (run ${ATTEMPT})" > "$RESULT_FILE"
                echo "Converged" > "$TASK_FILE"
                touch /etc/jellyship/.ansible-first-run
                break
            fi

            if [ "${FA:-0}" != "0" ]; then
                echo "run ${ATTEMPT}/5 failed=${FA} ok=${OK:-0}" > "$RESULT_FILE"
            else
                echo "run ${ATTEMPT}/5 changed=${CH} ok=${OK:-0}" > "$RESULT_FILE"
            fi
            echo "Retry ${ATTEMPT}/5..." > "$TASK_FILE"
            [ "$ATTEMPT" -lt 5 ] && sleep 5
        done

        # Final
        RECAP=$(grep -A1 "PLAY RECAP" "$LOG_DIR/ansible-latest.log" 2>/dev/null | tail -1)
        OK=$(echo "$RECAP" | grep -oP 'ok=\K\d+')
        CH=$(echo "$RECAP" | grep -oP 'changed=\K\d+')
        FA=$(echo "$RECAP" | grep -oP 'failed=\K\d+')
        if [ "${FA:-0}" = "0" ] && [ "${CH:-0}" = "0" ]; then
            echo "ok=${OK:-0} changed=0" > "$RESULT_FILE"
        elif [ "${FA:-0}" != "0" ]; then
            echo "FAILED ok=${OK:-0} changed=${CH:-0} failed=${FA}" > "$RESULT_FILE"
        else
            echo "ok=${OK:-0} changed=${CH:-0}" > "$RESULT_FILE"
        fi
        echo "Done" > "$TASK_FILE"
        rm -f /tmp/jellyship-ansible.pid
    ) &
    echo $! > /tmp/jellyship-ansible.pid
}

# ══════════════════════════════════════════════════════════════════════
# CHECK LAN vs WAN — offer certbot if WAN-facing
# ══════════════════════════════════════════════════════════════════════
is_rfc1918() {
    local ip="$1"
    case "$ip" in
        10.*|172.1[6-9].*|172.2[0-9].*|172.3[0-1].*|192.168.*) return 0 ;;
        *) return 1 ;;
    esac
}
is_ula_ipv6() {
    local ip="$1"
    case "$ip" in
        fc*|fd*) return 0 ;;
        *) return 1 ;;
    esac
}

# ══════════════════════════════════════════════════════════════════════
# PRE-RENDER DASHBOARD (no flicker — atomic swap)
# ══════════════════════════════════════════════════════════════════════
render_dashboard() {
    local buf=""
    local nl=$'\n'

    load_net_cache
    local UP=$(uptime -p 2>/dev/null | sed 's/^up /online /')

    # Banner + host info (3 lines)
    buf+="  \033[1;36m ╦╔═╗╦  ╦  ╦ ╦╔═╗╦ ╦╦╔═╗\033[0m${nl}"
    buf+="  \033[1;36m ║║╣ ║  ║  ╚╦╝╚═╗╠═╣║╠═╝\033[0m  hostname: $(hostname)${nl}"
    buf+="  \033[1;36m╚╝╚═╝╩═╝╩═╝ ╩ ╚═╝╩ ╩╩╩  \033[0m  ${UP}${nl}"
    buf+="${nl}"

    # Services (2 columns)
    buf+="  \033[1;37mServices\033[0m${nl}"
    local -a names=("jellyship" "jellyfin" "nginx" "jellyship-auth" "smbd" "jellyship-credentials" "unattended-upgrades" "sshd" "qemu-guest-agent")
    local -a labels=("JellyShip:9821" "Jellyfin:8096" "nginx:80" "AuthProxy:9199" "Samba:445" "CredSync" "AutoUpdate" "SSH:22" "QEMUAgent")

    local i=0
    while [ $i -lt ${#names[@]} ]; do
        local left="" right=""
        # Left
        if systemctl is-active --quiet "${names[$i]}" 2>/dev/null; then
            left="\033[1;32m●\033[0m ${labels[$i]}"
        elif systemctl is-enabled --quiet "${names[$i]}" 2>/dev/null; then
            left="\033[1;31m●\033[0m ${labels[$i]}"
        else
            left="\033[0;37m○\033[0m ${labels[$i]}"
        fi
        # Right
        local j=$((i + 1))
        if [ $j -lt ${#names[@]} ]; then
            if systemctl is-active --quiet "${names[$j]}" 2>/dev/null; then
                right="\033[1;32m●\033[0m ${labels[$j]}"
            elif systemctl is-enabled --quiet "${names[$j]}" 2>/dev/null; then
                right="\033[1;31m●\033[0m ${labels[$j]}"
            else
                right="\033[0;37m○\033[0m ${labels[$j]}"
            fi
        fi
        buf+="$(printf "  %-30b  %b" "$left" "$right")${nl}"
        i=$((i + 2))
    done

    # UFW
    if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -q "Status: active"; then
        buf+="  \033[1;32m●\033[0m UFW \033[0;37m(default deny, allow 22/80/445)\033[0m${nl}"
    elif command -v ufw >/dev/null 2>&1; then
        buf+="  \033[1;31m●\033[0m UFW \033[0;37m(inactive)\033[0m${nl}"
    else
        buf+="  \033[0;37m○ UFW (not installed)\033[0m${nl}"
    fi
    buf+="${nl}"

    # Network addresses (separate LAN/WAN lines)
    buf+="  \033[1;37mNetwork\033[0m${nl}"

    # IPv4
    if [ -n "$LAN4" ]; then
        local org4_tag=""
        [ -n "$ORG4" ] && org4_tag=" \033[0;37m(${ORG4})\033[0m"
        buf+="  \033[1;36mIPv4\033[0m LAN: ${LAN4}${nl}"
        buf+="       WAN: ${WAN4:-n/a}${org4_tag}${nl}"
    fi

    # IPv6
    if [ -n "$LAN6" ]; then
        local org6_tag=""
        [ -n "$ORG6" ] && org6_tag=" \033[0;37m(${ORG6})\033[0m"
        buf+="  \033[1;36mIPv6\033[0m LAN: ${LAN6}${nl}"
        buf+="       WAN: ${WAN6:-n/a}${org6_tag}${nl}"
    fi
    buf+="${nl}"

    # Access URLs
    buf+="  \033[1;37mAccess\033[0m${nl}"
    if [ -n "$LAN4" ]; then
        buf+="  \033[1;36mWeb:\033[0m http://${LAN4}  \033[0;37m│\033[0m  \033[1;36mAdmin:\033[0m http://${LAN4}/jellyship/${nl}"
        buf+="  \033[1;36mSSH:\033[0m jellyship@${LAN4}  \033[0;37m│\033[0m  \033[1;36mSMB:\033[0m \\\\\\\\${LAN4}\\\\Media${nl}"
    fi
    buf+="${nl}"

    # Credentials (one line)
    buf+="  \033[1;37mCredentials\033[0m  \033[0;37mjellyship / jellyship  (Jellyfin=master → PAM+SSH+SMB)\033[0m${nl}"
    buf+="${nl}"

    # Ansible status
    buf+="  \033[1;37mAnsible\033[0m  "
    LATEST_LOG="$LOG_DIR/ansible-latest.log"
    if [ -f "$LATEST_LOG" ]; then
        LOGTIME=$(stat -c %Y "$LATEST_LOG" 2>/dev/null)
        NOW=$(date +%s)
        AGE=$(( (NOW - ${LOGTIME:-$NOW}) / 60 ))
        [ $AGE -lt 1 ] && AGE_STR="<1m" || AGE_STR="${AGE}m"

        RECAP=$(grep -A1 "PLAY RECAP" "$LATEST_LOG" 2>/dev/null | tail -1)
        OK=$(echo "$RECAP" | grep -oP 'ok=\K\d+')
        CH=$(echo "$RECAP" | grep -oP 'changed=\K\d+')
        FA=$(echo "$RECAP" | grep -oP 'failed=\K\d+')

        if [ "${FA:-0}" = "0" ]; then
            buf+="\033[1;32m●\033[0m ${AGE_STR} ago  ok=${OK:-?} changed=${CH:-?}"
        else
            buf+="\033[1;31m●\033[0m ${AGE_STR} ago  ok=${OK:-?} changed=${CH:-?} \033[1;31mfailed=${FA}\033[0m"
        fi
    else
        buf+="\033[0;37m○ No runs yet\033[0m"
    fi
    buf+="${nl}"

    # Current ansible task
    if [ -f /tmp/jellyship-ansible.pid ] && kill -0 $(cat /tmp/jellyship-ansible.pid) 2>/dev/null; then
        CURRENT=$(cat "$TASK_FILE" 2>/dev/null | head -c 60)
        RESULT=$(cat "$RESULT_FILE" 2>/dev/null)
        buf+="  \033[1;33m▸\033[0m ${RESULT:-running}  \033[0;37m│\033[0m  ${CURRENT:-...}${nl}"
    fi
    buf+="${nl}"

    # Footer
    buf+="  \033[0;37mTTY2: Ctrl+Alt+F2  │  Manual: ansible-playbook ${PLAYBOOK}\033[0m${nl}"
    buf+="  \033[0;37mRefresh 5s  │  Auto-converge 15m  │  Logs: /var/log/jellyship/\033[0m${nl}"

    # Atomic swap
    echo -en "\033[H\033[J"
    echo -en "$buf"
}

# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════

# Initial network cache + ansible
refresh_net_cache
run_ansible_bg

CYCLE=0
while true; do
    render_dashboard

    # Refresh network cache every 60 cycles (5 min)
    if [ $((CYCLE % 60)) -eq 0 ] && [ $CYCLE -gt 0 ]; then
        refresh_net_cache
    fi

    # Re-run ansible every 180 cycles (15 min)
    if [ $((CYCLE % 180)) -eq 0 ] && [ $CYCLE -gt 0 ]; then
        run_ansible_bg
    fi

    # Auto-update check (every 60 cycles = 5 min)
    if [ $((CYCLE % 60)) -eq 0 ] && [ $CYCLE -gt 0 ] && [ -f /opt/jellyship/.git/HEAD ]; then
        (
            cd /opt/jellyship
            git config --global --add safe.directory /opt/jellyship 2>/dev/null
            git fetch origin --quiet 2>/dev/null
            LOCAL=$(git rev-parse HEAD 2>/dev/null)
            REMOTE=$(git rev-parse origin/main 2>/dev/null)
            if [ -n "$REMOTE" ] && [ "$LOCAL" != "$REMOTE" ]; then
                git pull --ff-only >/dev/null 2>&1
                npm install >/dev/null 2>&1
                npm run build >/dev/null 2>&1
                systemctl restart jellyship.service 2>/dev/null
            fi
        ) &
    fi

    CYCLE=$((CYCLE + 1))
    sleep 5
done
