#!/usr/bin/env bash
set -Eeuo pipefail

# FTS Display - AIC8800D80 Wi-Fi preparation
# Tested design:
#   Debian 12 / kernel 6.1: upstream pinned driver, no compatibility patch required.
#   Debian 13 / kernel 6.12: applies the cfg80211 monitor-channel compatibility fixes
#   proven during MCIT testing on 6.12.101+deb13-amd64.
#
# This script intentionally DOES NOT alter existing NetworkManager connections,
# SSIDs, passwords, routes, or the built-in Wi-Fi adapter.

SCRIPT_VERSION="1.0.0"
DRV_REPO="https://github.com/RicknotDev/aic8800d80.git"
DRV_COMMIT="99822dc79392d75502c613b4c463ed6557ada266"
DRV_WORK="/usr/local/src/aic8800d80-${DRV_COMMIT}"
DKMS_NAME="aic8800"
DKMS_VERSION="1.0.0"

log() { printf '\n[%s] %s\n' "$(date '+%F %T')" "$*"; }
die() { printf '\nERROR: %s\n' "$*" >&2; exit 1; }

[[ $EUID -eq 0 ]] || die "Run this script as root (for example: sudo bash $0)."

echo "=============================================================="
echo " FTS Display AIC8800D80 Wi-Fi Preparation v${SCRIPT_VERSION}"
echo "=============================================================="
echo "Host:       $(hostname)"
echo "OS:         $(awk -F= '/^PRETTY_NAME=/{gsub(/"/,"",$2); print $2}' /etc/os-release 2>/dev/null || true)"
echo "Kernel:     $(uname -r)"
echo "Arch:       $(uname -m)"
echo
echo "This script will NOT change existing Wi-Fi/network profiles."

command -v apt-get >/dev/null 2>&1 || die "This installer currently supports Debian/Ubuntu apt-based systems only."

log "Installing prerequisites"
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y \
    git \
    build-essential \
    dkms \
    "linux-headers-$(uname -r)" \
    eject \
    usb-modeswitch \
    rfkill \
    wpasupplicant \
    iw

[[ -e "/lib/modules/$(uname -r)/build" ]] || die "Kernel build tree is missing after header installation."

log "Fetching pinned AIC8800D80 source"
rm -rf "$DRV_WORK"
git clone "$DRV_REPO" "$DRV_WORK"
git -C "$DRV_WORK" checkout --detach "$DRV_COMMIT"
ACTUAL_COMMIT="$(git -C "$DRV_WORK" rev-parse HEAD)"
[[ "$ACTUAL_COMMIT" == "$DRV_COMMIT" ]] || die "Driver commit verification failed: got $ACTUAL_COMMIT"
echo "Verified driver commit: $ACTUAL_COMMIT"

# Kernel 6.12 changed cfg80211 set_monitor_channel to include net_device.
# The pinned driver switches several related call/declaration sites at 6.13.
# For 6.12.x only, change the known monitor-channel guards to 6.12.
kernel_ge_612=0
KMAJOR="$(uname -r | cut -d. -f1)"
KMINOR="$(uname -r | cut -d. -f2 | sed 's/[^0-9].*$//')"
if (( KMAJOR > 6 || (KMAJOR == 6 && KMINOR >= 12) )); then
    kernel_ge_612=1
fi

if (( kernel_ge_612 )); then
    log "Applying tested Linux 6.12+ cfg80211 compatibility patch"
    python3 - "$DRV_WORK" <<'PY'
from pathlib import Path
import sys

root = Path(sys.argv[1])
old = "KERNEL_VERSION(6, 13, 0)"
new = "KERNEL_VERSION(6, 12, 0)"

# Only alter guards associated with the monitor-channel API.
files = {
    root / "drivers/aic8800/aic8800_fdrv/rwnx_main.h": [
        "rwnx_cfg80211_set_monitor_channel_",
    ],
    root / "drivers/aic8800/aic8800_fdrv/aic_priv_cmd.h": [
        "set_mon_chan",
    ],
}

for path, needles in files.items():
    text = path.read_text()
    lines = text.splitlines(keepends=True)
    changed = 0
    for i, line in enumerate(lines):
        if old not in line:
            continue
        window = "".join(lines[i:i+8])
        if any(n in window for n in needles):
            lines[i] = line.replace(old, new)
            changed += 1
    if changed == 0:
        raise SystemExit(f"No expected compatibility guard found in {path}")
    path.write_text("".join(lines))
    print(f"{path.name}: patched {changed} guard(s)")

# aic_priv_cmd.c contains four related guards:
# declaration, set_mon_chan signature, call into cfg80211, and call site.
path = root / "drivers/aic8800/aic8800_fdrv/aic_priv_cmd.c"
lines = path.read_text().splitlines(keepends=True)
changed = 0
for i, line in enumerate(lines):
    if old not in line:
        continue
    context = "".join(lines[max(0, i-2):i+8])
    if ("rwnx_cfg80211_set_monitor_channel_" in context or
        "set_mon_chan" in context or
        "CMD_SET_MON_FREQ" in context):
        lines[i] = line.replace(old, new)
        changed += 1
if changed != 4:
    raise SystemExit(f"Expected 4 aic_priv_cmd.c monitor-channel guards, found {changed}")
path.write_text("".join(lines))
print(f"aic_priv_cmd.c: patched {changed} guards")

# rwnx_main.c has monitor-channel implementation/registration guards that
# also need the 6.12 boundary. Restrict changes to nearby monitor-channel text.
path = root / "drivers/aic8800/aic8800_fdrv/rwnx_main.c"
lines = path.read_text().splitlines(keepends=True)
changed = 0
for i, line in enumerate(lines):
    if old not in line:
        continue
    context = "".join(lines[max(0, i-8):i+12])
    if ("set_monitor_channel" in context or
        "rwnx_cfg80211_set_monitor_channel_" in context):
        lines[i] = line.replace(old, new)
        changed += 1
if changed == 0:
    raise SystemExit("No rwnx_main.c monitor-channel guard was patched")
path.write_text("".join(lines))
print(f"rwnx_main.c: patched {changed} monitor-channel guard(s)")
PY
else
    log "Kernel is older than 6.12; compatibility patch is not required"
fi

log "Removing any stale aic8800 DKMS registration"
dkms remove -m "$DKMS_NAME" -v "$DKMS_VERSION" --all >/dev/null 2>&1 || true
rm -rf "/usr/src/${DKMS_NAME}-${DKMS_VERSION}"

log "Running pinned vendor/community installer using prepared source"
cd "$DRV_WORK"
chmod +x ./install.sh
# Dependencies are already installed above. --skip-deps avoids it changing
# unrelated packages; --force permits known-supported kernel variants if the
# installer's version gate is conservative.
./install.sh --skip-deps --force

log "Ensuring firmware loader is requested at boot"
cat >/etc/modules-load.d/aic8800.conf <<'EOF'
aic_load_fw
EOF

# The driver supplies a udev rule that ejects the temporary AIC mass-storage
# personality (a69c:5721). The eject package installed above is essential.
udevadm control --reload-rules
udevadm trigger --subsystem-match=usb --action=add >/dev/null 2>&1 || true
depmod -a

log "Verification"
echo
echo "DKMS:"
dkms status | grep -i aic8800 || true

echo
echo "Installed modules:"
find "/lib/modules/$(uname -r)" \
    \( -iname 'aic_load_fw.ko*' -o -iname 'aic8800_fdrv.ko*' \) \
    -print 2>/dev/null || true

echo
echo "Required udev/eject pieces:"
command -v eject || true
ls -l /usr/lib/udev/rules.d/aic.rules 2>/dev/null || true
grep -nE 'a69c|5721|eject' /usr/lib/udev/rules.d/aic.rules 2>/dev/null || true

echo
echo "Existing NetworkManager connections (NOT modified):"
nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>/dev/null || true

echo
echo "Current wireless devices:"
iw dev 2>/dev/null || true

echo
echo "Current AIC USB identities, if dongle is present:"
lsusb 2>/dev/null | grep -Ei 'a69c:5721|368b:8d81|AIC|aicsemi' || echo "AIC dongle not currently present (this is OK)."

echo
echo "=============================================================="
echo " AIC8800D80 preparation complete"
echo "=============================================================="
echo
echo "No SSID, password, route, or existing Wi-Fi profile was changed."
echo "It is safe for the dongle to be plugged in after this script finishes."
echo
echo "When the dongle is available, verify with:"
echo "  lsusb"
echo "  iw dev"
echo "  nmcli device status"
