#!/usr/bin/env bash
set -euo pipefail
# POS: share nfs-server — Manage the NFS kernel server (status, share/unshare exports, enable/disable)
# POS_SUBCMDS: status share unshare list reload enable disable menu

source "$(dirname "$0")/../lib/common.sh" 2>/dev/null || source "$(dirname "$0")/common.sh"
source "$(dirname "$0")/../lib/notify.sh" 2>/dev/null || source "$(dirname "$0")/notify.sh"
source "$(dirname "$0")/../lib/share-lib.sh" 2>/dev/null || source "$(dirname "$0")/share-lib.sh"

command -v exportfs &>/dev/null || err "exportfs not found (install nfs-kernel-server)"

# Env seam (testable): which exports file is managed.
EXPORTS_FILE="${EXPORTS_FILE:-/etc/exports}"

usage() {
    cat <<EOF
Usage: pos share nfs-server <command> [args]

Manage the NFS kernel server (nfs-kernel-server, /etc/exports).

Commands:
  status              Show server status + current exports
  share <path> [client]  Add or update an export and reload
                        (default client: *(rw,sync,no_subtree_check))
  unshare <path>      Remove an export and reload
  list                Show current exports (exportfs -v)
  reload              Re-apply /etc/exports after hand edits
  enable              Start nfs-server and enable it on boot
  disable             Stop nfs-server and disable it on boot
  menu                Interactive browser (folder → client preset → share)

Run without arguments to open the interactive menu.

Clients are /etc/exports entries — restrict them to your trusted network:
  pos share nfs-server share /mnt/hdd '100.64.0.0/10(rw,sync,no_subtree_check)'   # Tailscale CGNAT
  pos share nfs-server share /mnt/hdd '10.10.0.0/24(rw,sync,no_subtree_check)'     # WireGuard
  pos share nfs-server share /mnt/backups '192.168.1.0/24(ro,sync,no_subtree_check)'

Examples:
  pos share nfs-server status
  pos share nfs-server share /mnt/hdd
  pos share nfs-server list
  pos share nfs-server unshare /mnt/hdd
  pos share nfs-server menu
EOF
    exit 0
}

cmd="${1:-}"
case "$cmd" in
    -h|--help) usage ;;
    ""|menu|status|share|unshare|list|reload|enable|disable) ;;
    *) err "Unknown command '$cmd' (see --help)" ;;
esac

require_root_dir() {
    local path="$1"
    case "$path" in
        /*) ;;
        *) err "Path must be absolute: $path" ;;
    esac
    [ -d "$path" ] || err "Path not found: $path"
}

cmd_status() {
    if systemctl is-active --quiet nfs-server 2>/dev/null; then
        ok "nfs-server: running"
    else
        warn "nfs-server: not running (enable with 'pos share nfs-server enable')"
    fi
    echo
    section "Exports"
    exportfs -v 2>/dev/null || echo " (none)"
}

cmd_share() {
    local path="$1" client="${2:-*(rw,sync,no_subtree_check)}"
    require_root_dir "$path"
    if [ "$client" = "*(rw,sync,no_subtree_check)" ]; then
        warn "Generic export '$client' — ANY client can mount $path. Restrict it, e.g.:"
        echo "  pos share nfs-server share $path '100.64.0.0/10(rw,sync,no_subtree_check)'"
        echo "  pos share nfs-server share $path '10.10.0.0/24(rw,sync,no_subtree_check)'"
        echo
    fi

    local tmp
    tmp="$(mktemp)"
    awk -v p="$path" '$1 != p' "$EXPORTS_FILE" > "$tmp"
    echo "$path $client" >> "$tmp"
    sudo cp "$tmp" "$EXPORTS_FILE"
    rm -f "$tmp"

    sudo exportfs -ra
    log "Exported: $path $client"
    notify_send "NFS share added: $path $client"

    # Advisory post-checks (never abort the share operation).
    if ! share_service_active nfs-server; then
        share_offer_fix "The nfs-server service is not running" \
            sudo systemctl enable --now nfs-server
    fi
    if share_ufw_blocks_ports '2049|111|\bnfs\b'; then
        warn "ufw is active but has no NFS rule — clients will be blocked."
        share_offer_fix "Allow NFS through ufw" sudo ufw allow 2049/tcp
    fi
}

cmd_unshare() {
    local path="$1"
    require_root_dir "$path"

    if ! awk -v p="$path" '$1 == p {found=1} END {exit !found}' "$EXPORTS_FILE"; then
        warn "No export for $path in $EXPORTS_FILE"
        exit 0
    fi

    local tmp
    tmp="$(mktemp)"
    awk -v p="$path" '$1 != p' "$EXPORTS_FILE" > "$tmp"
    sudo cp "$tmp" "$EXPORTS_FILE"
    rm -f "$tmp"

    sudo exportfs -ra
    log "Removed export: $path"
    notify_send "NFS share removed: $path"
}

cmd_list() {
    exportfs -v 2>/dev/null || echo "No exports"
}

cmd_reload() {
    sudo exportfs -ra
    log "NFS exports reloaded"
}

cmd_enable() {
    sudo systemctl enable --now nfs-server
    log "nfs-server enabled (starts on boot)"
    notify_send "NFS server enabled"
}

cmd_disable() {
    sudo systemctl disable --now nfs-server
    log "nfs-server disabled (will not start on boot)"
    notify_send "NFS server disabled"
}

# ── Interactive menu flows ─────────────────────────────────────
menu_pick_folder() { # stdout: folder path · rc 1 cancelled
    local idx dir cand
    local -a cands=()
    if mapfile -t cands < <(share_folder_candidates) && [ "${#cands[@]}" -gt 0 ]; then
        if idx="$(share_pick "Share which folder?" "${cands[@]}")"; then
            cand="${cands[$((idx - 1))]}"
            dir="${cand%% (*}"          # strip "(mounted fstype)" annotation
            [ -d "$dir" ] || { warn "Folder vanished: $dir"; return 1; }
            echo "$dir"
            return 0
        fi
        return 1
    fi
    dir="$(share_ask_value "Folder to share (absolute path)")" || return 1
    [ -n "$dir" ] || { warn "No folder given"; return 1; }
    echo "$dir"
}

menu_share() {
    local dir idx spec
    dir="$(menu_pick_folder)" || return 1

    local -a specs=(
        "100.64.0.0/10(rw,sync,no_subtree_check)  — Tailscale CGNAT range"
        "10.10.0.0/24(rw,sync,no_subtree_check)   — WireGuard subnet"
        "192.168.1.0/24(rw,sync,no_subtree_check) — LAN subnet"
        "192.168.1.0/24(ro,sync,no_subtree_check) — LAN read-only"
        "* (rw,sync,no_subtree_check)             — ANY client (unsafe)"
    )
    if idx="$(share_pick "Client access for ${dir}" "${specs[@]}")"; then
        spec="$(sed 's/[[:space:]]*—.*//' <<<"${specs[$((idx - 1))]}")"
        [ "$spec" = "*" ] && spec="*(rw,sync,no_subtree_check)"
    else
        spec="$(share_ask_value "Client spec (e.g. 10.10.0.0/24(rw,sync))" "")" || return 1
        [ -n "$spec" ] || spec="*(rw,sync,no_subtree_check)"
    fi
    cmd_share "$dir" "$spec"
}

menu_unshare() {
    local idx path
    local -a paths=()
    if mapfile -t paths < <(awk 'NF > 0 && $1 !~ /^#/ {print $1}' "$EXPORTS_FILE" 2>/dev/null) &&
        [ "${#paths[@]}" -gt 0 ]; then
        idx="$(share_pick "Remove which export?" "${paths[@]}")" || return 1
        path="${paths[$((idx - 1))]}"
    else
        path="$(share_ask_value "Exported path to remove")" || return 1
        [ -n "$path" ] || return 1
    fi
    cmd_unshare "$path"
}

run_menu() {
    share_menu_guard || exit 1
    while true; do
        local choice
        choice="$(share_menu_run "NFS server" \
            "Show status (service + exports)" \
            "Share a folder" \
            "Remove an export" \
            "List current exports" \
            "Reload exports after hand edits" \
            "Enable service on boot" \
            "Disable service")" || return 0
        case "$choice" in
            1) cmd_status ;;
            2) menu_share ;;
            3) menu_unshare ;;
            4) cmd_list ;;
            5) cmd_reload ;;
            6) cmd_enable ;;
            7) cmd_disable ;;
        esac
    done
}

case "$cmd" in
    ""|menu)
        run_menu
        exit 0
        ;;
    status)  cmd_status ;;
    share)
        [ $# -ge 2 ] || err "Usage: pos share nfs-server share <path> [client]"
        cmd_share "$2" "${3:-*(rw,sync,no_subtree_check)}"
        ;;
    unshare)
        [ $# -ge 2 ] || err "Usage: pos share nfs-server unshare <path>"
        cmd_unshare "$2"
        ;;
    list)    cmd_list ;;
    reload)  cmd_reload ;;
    enable)  cmd_enable ;;
    disable) cmd_disable ;;
esac
