#!/usr/bin/env bash
set -euo pipefail
# POS: system firewall — Interactive UFW management
IFS=$'\n\t'

if [[ $EUID -ne 0 ]]; then
    echo "ERROR: Please run as root (sudo)."
    echo "Usage: sudo pos system firewall"
    exit 1
fi

HISTORY=()
DRY_RUN=0

if [[ "${1:-}" == "--dry-run" ]]; then
    DRY_RUN=1
fi

log()  { echo "[+] $*"; }
warn() { echo "[!] $*"; }
err()  { echo "ERROR: $*" >&2; exit 1; }

run_cmd() {
    local -a cmd=("$@")
    printf "\n>>> %s\n" "${cmd[*]}"
    read -rp "Execute this command? [y/N]: " confirm
    if [[ "$confirm" =~ ^[Yy]$ ]]; then
        if [[ "$DRY_RUN" -eq 1 ]]; then
            echo "(dry-run) skipping execution"
        else
            "${cmd[@]}"
        fi
        HISTORY+=("${cmd[*]}")
    else
        echo "Cancelled."
    fi
}

build_ufw_cmd() {
    local action="$1"
    local direction="$2"
    local proto="$3"
    local from="$4"
    local to="$5"
    local port="$6"
    local onif="$7"
    local logmode="$8"
    local comment="$9"
    local insert_pos="${10:-}"
    local suffix="${11:-}"

    local -a cmd=(ufw)

    if [[ -n "$insert_pos" ]]; then
        if [[ "$insert_pos" == "prepend" ]]; then
            cmd+=(prepend)
        else
            cmd+=(insert "$insert_pos")
        fi
    fi

    cmd+=("$action")

    [[ -n "$direction" ]] && cmd+=("$direction")
    [[ -n "$onif" ]]      && cmd+=(on "$onif")
    [[ -n "$proto" ]]     && cmd+=(proto "$proto")
    [[ -n "$from" ]]      && cmd+=(from "$from")
    cmd+=(to "$to")
    [[ -n "$port" ]]      && cmd+=(port "$port")
    [[ -n "$logmode" ]]   && cmd+=("$logmode")
    if [[ -n "$comment" ]]; then
        local safe="${comment// /_}"
        cmd+=(comment "$safe")
    fi
    [[ "$suffix" == "v6" ]] && cmd+=(v6)

    run_cmd "${cmd[@]}"
}

prompt_ipver() {
    local ver
    read -rp "IP version (4 / 6 / both): " ver
    echo "$ver"
}

apply_for_versions() {
    local action="$1" direction="$2" proto="$3" from="$4" to="$5"
    local port="$6" onif="$7" logmode="$8" comment="$9"
    local insert_pos="${10:-}"
    local ipver
    ipver=$(prompt_ipver)

    case "$ipver" in
        4)    build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "" ;;
        6)    build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "v6" ;;
        both)
            build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" ""
            build_ufw_cmd "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos" "v6"
            ;;
        *)  echo "Invalid choice. Choose 4, 6 or both." ;;
    esac
}

add_rule() {
    echo
    echo "Choose rule type:"
    echo "1) Port/service (eg: port 8080 or 'ssh')"
    echo "2) IP-based (from X to Y)"
    echo "3) Directional port rule (in/out to any port ...)"
    read -rp "Choice: " rtype

    case "$rtype" in
        1)
            read -rp "Action (allow/deny/reject/limit) [allow]: " action
            action=${action:-allow}
            read -rp "Enter port number or service name (eg 'ssh' or '8080'): " port_or_svc

            if [[ "$port_or_svc" =~ ^[0-9]+$ ]]; then
                read -rp "Protocol (tcp/udp/any) [tcp]: " proto
                proto=${proto:-tcp}
                [[ "$proto" == "any" ]] && proto=""
                read -rp "Interface (leave empty for any): " onif
                read -rp "Log? (none/log/log-all) [none]: " logmode
                [[ "$logmode" == "none" ]] && logmode=""
                read -rp "Comment (optional): " comment

                apply_for_versions "$action" "" "$proto" "" "any" "$port_or_svc" "$onif" "$logmode" "$comment"
            else
                read -rp "IP version (4 / 6 / both) [4]: " ipver
                ipver=${ipver:-4}
                case "$ipver" in
                    4)    run_cmd ufw "$action" "$port_or_svc" ;;
                    6)    run_cmd ufw "$action" "$port_or_svc" v6 ;;
                    both) run_cmd ufw "$action" "$port_or_svc"
                          run_cmd ufw "$action" "$port_or_svc" v6 ;;
                    *)    echo "invalid ipver" ;;
                esac
            fi
            ;;

        2)
            read -rp "Action (allow/deny/reject) [deny]: " action
            action=${action:-deny}
            read -rp "From address/CIDR (eg 192.168.1.5 or 10.0.0.0/24): " from
            read -rp "To address (leave empty for 'any') [any]: " to
            to=${to:-any}
            read -rp "Direction (in/out) [in]: " direction
            direction=${direction:-in}
            read -rp "Port (leave empty if not applicable): " port
            read -rp "Protocol (tcp/udp/any) [any]: " proto
            [[ "$proto" == "any" ]] && proto=""
            read -rp "Interface (leave empty for any): " onif
            read -rp "Log? (none/log/log-all) [none]: " logmode
            [[ "$logmode" == "none" ]] && logmode=""
            read -rp "Comment (optional): " comment

            apply_for_versions "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment"
            ;;

        3)
            read -rp "Action (allow/deny/reject/limit) [allow]: " action
            action=${action:-allow}
            read -rp "Direction (in/out) [in]: " direction
            direction=${direction:-in}
            read -rp "Port number: " port
            read -rp "Protocol (tcp/udp/any) [tcp]: " proto
            [[ "$proto" == "any" ]] && proto=""
            read -rp "On interface (leave empty for any): " onif
            read -rp "From address (optional): " from
            from=${from:-}
            read -rp "To address [any]: " to
            to=${to:-any}
            read -rp "Log? (none/log/log-all) [none]: " logmode
            [[ "$logmode" == "none" ]] && logmode=""
            read -rp "Comment (optional): " comment
            read -rp "Insert position (number/prepend/empty): " insert_pos

            apply_for_versions "$action" "$direction" "$proto" "$from" "$to" "$port" "$onif" "$logmode" "$comment" "$insert_pos"
            ;;

        *)  echo "Unknown choice." ;;
    esac
}

delete_rule() {
    echo
    echo "Delete rule by:"
    echo "1) rule number (use 'ufw status numbered' to see numbers)"
    echo "2) rule text (eg: 'allow 22/tcp')"
    read -rp "Choice: " dch

    case "$dch" in
        1)
            ufw status numbered
            read -rp "Number to delete: " num
            run_cmd ufw delete "$num"
            ;;
        2)
            read -rp "Exact rule text to delete (eg: deny 80/tcp): " ruletext
            run_cmd ufw delete $ruletext
            ;;
        *)  echo "Unknown choice." ;;
    esac
}

show_status() {
    echo
    echo "1) Simple status"
    echo "2) Verbose status"
    echo "3) Numbered status (useful for delete)"
    read -rp "Choice: " sc
    case "$sc" in
        1) run_cmd ufw status ;;
        2) run_cmd ufw status verbose ;;
        3) run_cmd ufw status numbered ;;
        *) echo "Unknown choice." ;;
    esac
}

while true; do
    cat <<'MENU'

==============================
   UFW POWER — human friendly
==============================
1) Add rule (port/service/ip/directional)
2) Delete rule (by number or text)
3) Show status (simple / verbose / numbered)
4) Enable UFW
5) Disable UFW
6) Reset UFW (delete all rules)
7) Set default policy (incoming/outgoing)
8) Show executed commands history (so far)
0) Exit
------------------------------
MENU
    read -rp "Choose: " opt

    case "$opt" in
        1) add_rule ;;
        2) delete_rule ;;
        3) show_status ;;
        4) run_cmd ufw enable ;;
        5) run_cmd ufw disable ;;
        6)
            echo "WARNING: ufw reset will disable and remove all rules."
            read -rp "Type 'RESET' to confirm: " c
            [[ "$c" == "RESET" ]] && run_cmd ufw reset || echo "Reset aborted."
            ;;
        7)
            read -rp "Default incoming policy (allow/deny/reject) [deny]: " defin
            defin=${defin:-deny}
            read -rp "Default outgoing policy (allow/deny/reject) [allow]: " defout
            defout=${defout:-allow}
            run_cmd ufw default "$defin" incoming
            run_cmd ufw default "$defout" outgoing
            ;;
        8)
            echo
            echo "Executed commands so far:"
            echo
            if [[ "${#HISTORY[@]}" -eq 0 ]]; then
                echo "(none yet)"
            else
                for c in "${HISTORY[@]}"; do echo " - $c"; done
            fi
            ;;
        0)
            echo
            echo "Final executed commands summary:"
            if [[ "${#HISTORY[@]}" -eq 0 ]]; then
                echo "(no commands executed)"
            else
                for c in "${HISTORY[@]}"; do echo " - $c"; done
            fi
            echo "Goodbye — firewall remains watchful."
            exit 0
            ;;
        *) echo "Unknown option." ;;
    esac

    echo
    read -rp "Press Enter to continue..."
    clear
done
