#!/bin/bash
# BDUSP DDoS Guard CLI v5

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; NC='\033[0m'; BOLD='\033[1m'

BASE="/var/lib/ddos-guard"
BLOCKLIST="$BASE/blocklist.json"
WHITELIST="$BASE/whitelist.json"
CONFIG="$BASE/config.json"
QUARANTINE="$BASE/quarantine.json"
LOGFILE="/var/log/ddos-guard.log"

header() {
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "${CYAN}  🛡️  BDUSP DDoS Guard v5${NC}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}

py() { python3 -c "$1"; }

valid_ip() {
    python3 -c "
import re,sys
ip='$1'.strip()
m=re.match(r'^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$',ip)
if m and all(0<=int(g)<=255 for g in m.groups()): sys.exit(0)
sys.exit(1)
" 2>/dev/null
}

cfg_get() {
    py "
import json
try: d=json.load(open('$CONFIG'))
except: d={}
defs={'window_seconds':30,'block_threshold':300,'perm_threshold':1000,'check_interval':5,'quarantine_single_ip':300,'quarantine_all_ip':3000}
defs.update(d); print(defs.get('$1',''))
"
}

# ── vg status ──────────────────────────────────────────────────────────────────
cmd_status() {
    header; echo ""
    if systemctl is-active --quiet ddos-guard; then
        echo -e "  Service   : ${GREEN}● Running${NC}"
    else
        echo -e "  Service   : ${RED}● Stopped${NC}"
    fi

    total=$(py "import json
try: d=json.load(open('$BLOCKLIST')); print(len(d))
except: print(0)")
    temp=$(py "import json
try: d=json.load(open('$BLOCKLIST')); print(sum(1 for v in d.values() if not v.get('permanent')))
except: print(0)")
    perm=$(py "import json
try: d=json.load(open('$BLOCKLIST')); print(sum(1 for v in d.values() if v.get('permanent')))
except: print(0)")
    wl=$(py "import json
try: d=json.load(open('$WHITELIST')); print(len(d))
except: print(0)")
    qt=$(py "import json
try: d=json.load(open('$QUARANTINE')); print(len(d))
except: print(0)")

    echo -e "  Blocked   : ${RED}${total} IP(s)${NC} — ${temp} temp, ${perm} permanent"
    echo -e "  Whitelist : ${GREEN}${wl} IP(s)${NC}"
    echo -e "  Quarantine: ${YELLOW}${qt} domain(s)${NC}"
    echo ""
    echo -e "  ${BOLD}IP Block Thresholds (per $(cfg_get window_seconds)s window):${NC}"
    echo -e "  7-day block     : $(cfg_get block_threshold) requests"
    echo -e "  Permanent block : $(cfg_get perm_threshold) requests"
    echo -e "  Check interval  : $(cfg_get check_interval)s"
    echo ""
    echo -e "  ${BOLD}Quarantine Thresholds (per $(cfg_get window_seconds)s window):${NC}"
    echo -e "  Single IP       : $(cfg_get quarantine_single_ip) requests"
    echo -e "  All IPs total   : $(cfg_get quarantine_all_ip) requests"
    echo ""
}

# ── vg config ──────────────────────────────────────────────────────────────────
cmd_config() {
    if [ -z "$1" ]; then
        header; echo ""
        echo -e "  ${BOLD}Current Config:${NC}"; echo ""
        echo -e "  ${GREEN}window_seconds${NC}         $(cfg_get window_seconds)     Monitoring window (seconds)"
        echo -e "  ${GREEN}check_interval${NC}         $(cfg_get check_interval)     Log check every N seconds"
        echo ""
        echo -e "  ${BOLD}IP Block:${NC}"
        echo -e "  ${GREEN}block_threshold${NC}        $(cfg_get block_threshold)   Requests → 7-day block"
        echo -e "  ${GREEN}perm_threshold${NC}         $(cfg_get perm_threshold)  Requests → permanent block"
        echo ""
        echo -e "  ${BOLD}Quarantine:${NC}"
        echo -e "  ${GREEN}quarantine_single_ip${NC}   $(cfg_get quarantine_single_ip)   Single IP requests → domain quarantine"
        echo -e "  ${GREEN}quarantine_all_ip${NC}      $(cfg_get quarantine_all_ip)  All IPs combined → domain quarantine"
        echo ""
        echo -e "  ${YELLOW}Usage: vg config <key> <value>${NC}"
        echo -e "  Example: vg config quarantine_single_ip 200"
        echo ""
        return
    fi
    [ -z "$2" ] && echo -e "${RED}Usage: vg config <key> <value>${NC}" && exit 1
    py "
import json,os
os.makedirs('$BASE',exist_ok=True)
try: d=json.load(open('$CONFIG'))
except: d={}
v='$2'
try: v=int(v)
except: pass
d['$1']=v
json.dump(d,open('$CONFIG','w'),indent=2)
print('  ✅ Set $1 =',v)
"
    systemctl restart ddos-guard 2>/dev/null
    echo -e "${GREEN}✅ Config saved. Service restarted.${NC}"
}

# ── BLOCKLIST ──────────────────────────────────────────────────────────────────

_show_blocklist() {
    local filter="$1"
    py "
import json,time
try: data=json.load(open('$BLOCKLIST'))
except: data={}
filt='$filter'
rows=[]
for ip,i in data.items():
    perm=i.get('permanent',False)
    if filt=='temp' and perm: continue
    if filt=='perm' and not perm: continue
    bt=time.strftime('%Y-%m-%d %H:%M',time.localtime(i.get('time',0)))
    if perm: ex='Never'
    else:
        exp=i.get('time',0)+(i.get('duration') or 0)
        ex=time.strftime('%Y-%m-%d %H:%M',time.localtime(exp))
    typ='PERMANENT' if perm else '7 Days'
    src='manual' if not i.get('auto') else 'auto'
    rows.append((ip,typ,bt,ex,src))
if not rows: print('  No entries.'); exit()
print(f\"  {'IP':<20} {'Type':<12} {'Blocked At':<18} {'Expires':<18} {'Source'}\")
print(f\"  {'─'*20} {'─'*12} {'─'*18} {'─'*18} {'─'*8}\")
for r in rows:
    print(f'  {r[0]:<20} {r[1]:<12} {r[2]:<18} {r[3]:<18} {r[4]}')
print()
"
}

cmd_blocklist()      { header; echo -e "\n  ${BOLD}All Blocked IPs:${NC}\n"; _show_blocklist all; }
cmd_blocklist_temp() { header; echo -e "\n  ${BOLD}Temp Blocked IPs (7-day):${NC}\n"; _show_blocklist temp; }
cmd_blocklist_perm() { header; echo -e "\n  ${BOLD}Permanently Blocked IPs:${NC}\n"; _show_blocklist perm; }

_add_ips_to_blocklist() {
    local perm="$1"; shift
    local raw="$*"
    IFS=', ' read -ra ips <<< "$raw"
    local invalid=(); local added=()
    for ip in "${ips[@]}"; do
        ip=$(echo "$ip" | tr -d ',' | xargs)
        [ -z "$ip" ] && continue
        if ! valid_ip "$ip"; then invalid+=("$ip")
        else added+=("$ip")
        fi
    done
    [ ${#invalid[@]} -gt 0 ] && echo -e "${RED}Invalid IP(s): ${invalid[*]}${NC}"
    [ ${#added[@]} -eq 0 ] && echo -e "${YELLOW}No valid IPs to add.${NC}" && return
    for ip in "${added[@]}"; do
        iptables -I INPUT -s "$ip" -j DROP 2>/dev/null
        iptables -I OUTPUT -d "$ip" -j DROP 2>/dev/null
        python3 -c "
import json,time,os
os.makedirs('$BASE',exist_ok=True)
try: d=json.load(open('$BLOCKLIST'))
except: d={}
perm=$perm
d['$ip']={'time':time.time(),'duration':None if perm else 604800,'permanent':perm,'label':'PERMANENT' if perm else '7d','auto':False}
json.dump(d,open('$BLOCKLIST','w'),indent=2)
"
        echo -e "${GREEN}  ✅ Blocked: $ip${NC}"
    done
}

cmd_blocklist_add()      { [ -z "$1" ] && echo -e "${RED}Usage: vg blocklist-add <ip> [ip2]...${NC}" && exit 1; _add_ips_to_blocklist True "$@"; }
cmd_blocklist_add_temp() { [ -z "$1" ] && echo -e "${RED}Usage: vg blocklist-add-temp <ip> [ip2]...${NC}" && exit 1; _add_ips_to_blocklist False "$@"; }

_remove_ips() {
    local raw="$*"
    IFS=', ' read -ra ips <<< "$raw"
    local invalid=()
    for ip in "${ips[@]}"; do
        ip=$(echo "$ip" | tr -d ',' | xargs)
        [ -z "$ip" ] && continue
        if ! valid_ip "$ip"; then
            invalid+=("$ip")
        else
            iptables -D INPUT -s "$ip" -j DROP 2>/dev/null
            iptables -D OUTPUT -d "$ip" -j DROP 2>/dev/null
            python3 -c "
import json
try: d=json.load(open('$BLOCKLIST')); d.pop('$ip',None); json.dump(d,open('$BLOCKLIST','w'),indent=2)
except: pass
"
            echo -e "${GREEN}  ✅ Removed: $ip${NC}"
        fi
    done
    [ ${#invalid[@]} -gt 0 ] && echo -e "${RED}Invalid IP(s): ${invalid[*]}${NC}"
}

cmd_blocklist_remove() { [ -z "$1" ] && echo -e "${RED}Usage: vg blocklist-remove <ip> [ip2]${NC}" && exit 1; _remove_ips "$@"; }

cmd_blocklist_remove_all() {
    echo -e "${YELLOW}Removing ALL blocked IPs...${NC}"
    py "
import json,subprocess
try: data=json.load(open('$BLOCKLIST'))
except: data={}
for ip in data:
    subprocess.run('iptables -D INPUT -s {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    subprocess.run('iptables -D OUTPUT -d {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
json.dump({},open('$BLOCKLIST','w'),indent=2)
print('  Removed {} IP(s).'.format(len(data)))
"
    echo -e "${GREEN}✅ Done.${NC}"
}

cmd_blocklist_remove_all_temp() {
    echo -e "${YELLOW}Removing all temp (7-day) blocked IPs...${NC}"
    py "
import json,subprocess
try: data=json.load(open('$BLOCKLIST'))
except: data={}
to_del=[ip for ip,i in data.items() if not i.get('permanent')]
for ip in to_del:
    subprocess.run('iptables -D INPUT -s {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    subprocess.run('iptables -D OUTPUT -d {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    del data[ip]
json.dump(data,open('$BLOCKLIST','w'),indent=2)
print('  Removed {} IP(s).'.format(len(to_del)))
"
    echo -e "${GREEN}✅ Done.${NC}"
}

cmd_blocklist_remove_all_perm() {
    echo -e "${YELLOW}Removing all permanently blocked IPs...${NC}"
    py "
import json,subprocess
try: data=json.load(open('$BLOCKLIST'))
except: data={}
to_del=[ip for ip,i in data.items() if i.get('permanent')]
for ip in to_del:
    subprocess.run('iptables -D INPUT -s {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    subprocess.run('iptables -D OUTPUT -d {} -j DROP 2>/dev/null'.format(ip),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    del data[ip]
json.dump(data,open('$BLOCKLIST','w'),indent=2)
print('  Removed {} IP(s).'.format(len(to_del)))
"
    echo -e "${GREEN}✅ Done.${NC}"
}

# ── WHITELIST ──────────────────────────────────────────────────────────────────

cmd_whitelist() {
    header; echo -e "\n  ${BOLD}Whitelisted IPs:${NC}\n"
    py "
import json,time
sys_ips={'127.0.0.1':{'added':0,'note':'system'},'::1':{'added':0,'note':'system'}}
try: u=json.load(open('$WHITELIST')); sys_ips.update(u)
except: pass
print(f\"  {'IP':<22} {'Added':<20} {'Note'}\")
print(f\"  {'─'*22} {'─'*20} {'─'*20}\")
for ip,i in sys_ips.items():
    added=time.strftime('%Y-%m-%d %H:%M',time.localtime(i.get('added',0))) if i.get('added') else 'system'
    print(f\"  {ip:<22} {added:<20} {i.get('note','')}\")
print()
"
}

_add_ips_to_whitelist() {
    local raw="$*"
    IFS=', ' read -ra ips <<< "$raw"
    local invalid=(); local added=()
    for ip in "${ips[@]}"; do
        ip=$(echo "$ip" | tr -d ',' | xargs)
        [ -z "$ip" ] && continue
        if ! valid_ip "$ip"; then invalid+=("$ip")
        else added+=("$ip")
        fi
    done
    [ ${#invalid[@]} -gt 0 ] && echo -e "${RED}Invalid IP(s): ${invalid[*]}${NC}"
    [ ${#added[@]} -eq 0 ] && echo -e "${YELLOW}No valid IPs.${NC}" && return
    for ip in "${added[@]}"; do
        python3 -c "
import json,time,os
os.makedirs('$BASE',exist_ok=True)
try: d=json.load(open('$WHITELIST'))
except: d={}
d['$ip']={'added':time.time(),'note':'manual'}
json.dump(d,open('$WHITELIST','w'),indent=2)
"
        echo -e "${GREEN}  ✅ Whitelisted: $ip${NC}"
    done
}

_remove_ips_from_whitelist() {
    local raw="$*"
    IFS=', ' read -ra ips <<< "$raw"
    for ip in "${ips[@]}"; do
        ip=$(echo "$ip" | tr -d ',' | xargs)
        [ -z "$ip" ] && continue
        if ! valid_ip "$ip"; then
            echo -e "${RED}Invalid IP: $ip${NC}"
        else
            python3 -c "
import json
try: d=json.load(open('$WHITELIST')); d.pop('$ip',None); json.dump(d,open('$WHITELIST','w'),indent=2)
except: pass
"
            echo -e "${GREEN}  ✅ Removed: $ip${NC}"
        fi
    done
}

cmd_whitelist_add()        { [ -z "$1" ] && echo -e "${RED}Usage: vg whitelist-add <ip> [ip2]${NC}" && exit 1; _add_ips_to_whitelist "$@"; }
cmd_whitelist_remove()     { [ -z "$1" ] && echo -e "${RED}Usage: vg whitelist-remove <ip> [ip2]${NC}" && exit 1; _remove_ips_from_whitelist "$@"; }
cmd_whitelist_remove_all() {
    py "import json; json.dump({},open('$WHITELIST','w'),indent=2); print('  Whitelist cleared.')"
    echo -e "${GREEN}✅ Done.${NC}"
}

# ── QUARANTINE INLINE PYTHON HELPERS ──────────────────────────────────────────
# No module import — all logic embedded directly here

QUARANTINE_PY_HELPERS='
import os, json, time, subprocess

BASE        = "/var/lib/ddos-guard"
QFILE       = BASE + "/quarantine.json"
CHALLENGE   = ".bdusp_ddos_challanges.php"

def _run(cmd):
    subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def _load(path, default):
    try:
        return json.load(open(path))
    except:
        return default

def _save(path, data):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    json.dump(data, open(path, "w"), indent=2)

def _get_cpanel_users():
    users = []
    try:
        for e in os.scandir("/var/cpanel/users"):
            if e.is_file():
                users.append(e.name)
    except:
        pass
    return users

def _get_docroot(domain):
    domain = domain.strip().lower().lstrip("www.")
    for user in _get_cpanel_users():
        homedir = "/home/" + user
        try:
            for line in open("/var/cpanel/users/" + user).read().splitlines():
                if line.startswith("DNS="):
                    primary = line.split("=", 1)[1].strip().lower().lstrip("www.")
                    if primary == domain:
                        return os.path.join(homedir, "public_html"), user
        except:
            pass
        ud_dir = "/var/cpanel/userdata/" + user
        if not os.path.isdir(ud_dir):
            continue
        for fname in os.listdir(ud_dir):
            if fname.endswith("_SSL") or fname == "main":
                continue
            if fname.strip().lower().lstrip("www.") == domain:
                try:
                    for line in open(os.path.join(ud_dir, fname)).read().splitlines():
                        if line.strip().startswith("documentroot:"):
                            return line.split(":", 1)[1].strip(), user
                except:
                    pass
    return None, None

def _htaccess():
    return """RewriteEngine On

# Static files skip
RewriteCond %{REQUEST_URI} !\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|map|webp)$ [NC]

# Challenge file loop করবে না
RewriteCond %{REQUEST_URI} !^/""" + CHALLENGE + """$

# সব traffic → challenge
RewriteRule ^(.*)$ /""" + CHALLENGE + """ [R=302,L]
"""

def _challenge_php():
    return r"""<?php
session_start();
if (isset($_SESSION["ddos_verified"]) && $_SESSION["ddos_verified"] === true) {
    header("Location: /"); exit;
}
if (!isset($_SESSION["num1"])) {
    $_SESSION["num1"] = rand(1, 20);
    $_SESSION["num2"] = rand(1, 20);
}
$error = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $answer  = (int)$_POST["answer"];
    $correct = $_SESSION["num1"] + $_SESSION["num2"];
    if ($answer === $correct) {
        $_SESSION["ddos_verified"] = true;
        unset($_SESSION["num1"], $_SESSION["num2"]);
        header("Location: /"); exit;
    } else {
        $error = "Wrong answer. Try again.";
        $_SESSION["num1"] = rand(1, 20);
        $_SESSION["num2"] = rand(1, 20);
    }
}
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Security Check</title>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{min-height:100vh;background:#0a0f1e;display:flex;align-items:center;justify-content:center;font-family:"Segoe UI",system-ui,sans-serif;padding:20px}
.card{background:#111827;border:1px solid #1f2937;border-radius:16px;padding:48px 40px;width:100%;max-width:420px;text-align:center}
.shield{width:64px;height:64px;background:linear-gradient(135deg,#1d4ed8,#3b82f6);border-radius:50%;display:flex;align-items:center;justify-content:center;margin:0 auto 24px;font-size:28px}
h1{color:#f9fafb;font-size:22px;font-weight:700;margin-bottom:8px}
.subtitle{color:#6b7280;font-size:14px;margin-bottom:32px;line-height:1.5}
.challenge-box{background:#0f172a;border:1px solid #1e3a5f;border-radius:12px;padding:20px;margin-bottom:20px}
.challenge-label{color:#9ca3af;font-size:12px;text-transform:uppercase;letter-spacing:1px;margin-bottom:10px}
.challenge-math{color:#60a5fa;font-size:28px;font-weight:700;letter-spacing:2px}
input[type=number]{width:100%;padding:14px 16px;background:#1f2937;border:1px solid #374151;border-radius:10px;color:#f9fafb;font-size:16px;text-align:center;outline:none;transition:border-color .2s;margin-bottom:12px;-moz-appearance:textfield}
input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{-webkit-appearance:none}
input[type=number]:focus{border-color:#3b82f6}
button{width:100%;padding:14px;background:linear-gradient(135deg,#1d4ed8,#3b82f6);color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;transition:opacity .2s}
button:hover{opacity:.9}
.error{color:#f87171;font-size:13px;margin-top:12px;background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:8px;padding:10px}
.footer{color:#374151;font-size:12px;margin-top:28px}
</style>
</head>
<body>
<div class="card">
  <div class="shield">&#x1F6E1;&#xFE0F;</div>
  <h1>Security Verification</h1>
  <p class="subtitle">Unusual traffic detected from your network.<br>Please complete the challenge to continue.</p>
  <div class="challenge-box">
    <div class="challenge-label">Solve to verify</div>
    <div class="challenge-math"><?= $_SESSION["num1"] ?> + <?= $_SESSION["num2"] ?> = ?</div>
  </div>
  <form method="POST">
    <input type="number" name="answer" placeholder="Enter your answer" autofocus required>
    <button type="submit">Verify &amp; Continue &#x2192;</button>
  </form>
  <?php if($error): ?><div class="error">&#x2717; <?= htmlspecialchars($error) ?></div><?php endif; ?>
  <div class="footer">Protected by BDUSP DDoS Guard</div>
</div>
</body>
</html>
"""

def q_on(domain, reason="manual"):
    docroot, user = _get_docroot(domain)
    if not docroot:
        print("\033[0;31m  ✗ Domain not found in cPanel: " + domain + "\033[0m")
        return False
    htaccess  = os.path.join(docroot, ".htaccess")
    backup    = os.path.join(docroot, ".htaccess_backup")
    challenge = os.path.join(docroot, CHALLENGE)
    try:
        if os.path.isfile(htaccess) and not os.path.isfile(backup):
            os.rename(htaccess, backup)
        open(htaccess, "w").write(_htaccess())
        open(challenge, "w").write(_challenge_php())
        if user:
            _run("chown " + user + ":" + user + " " + htaccess + " " + challenge + " 2>/dev/null")
            _run("chmod 644 " + htaccess + " " + challenge + " 2>/dev/null")
        ql = _load(QFILE, {})
        ql[domain] = {"time": time.time(), "trigger_count": 0, "reason": reason, "docroot": docroot, "user": user or ""}
        _save(QFILE, ql)
        return True
    except Exception as e:
        print("\033[0;31m  ✗ Error: " + str(e) + "\033[0m")
        return False

def q_off(domain):
    ql   = _load(QFILE, {})
    info = ql.get(domain, {})
    docroot = info.get("docroot") if info else None
    user    = info.get("user") if info else None
    if not docroot:
        docroot, user = _get_docroot(domain)
    if not docroot:
        print("\033[0;31m  ✗ Domain not found: " + domain + "\033[0m")
        return False
    htaccess  = os.path.join(docroot, ".htaccess")
    backup    = os.path.join(docroot, ".htaccess_backup")
    challenge = os.path.join(docroot, CHALLENGE)
    try:
        if os.path.isfile(htaccess):
            os.remove(htaccess)
        if os.path.isfile(backup):
            os.rename(backup, htaccess)
            if user:
                _run("chown " + user + ":" + user + " " + htaccess + " 2>/dev/null")
        if os.path.isfile(challenge):
            os.remove(challenge)
        ql.pop(domain, None)
        _save(QFILE, ql)
        return True
    except Exception as e:
        print("\033[0;31m  ✗ Error: " + str(e) + "\033[0m")
        return False
'

# ── QUARANTINE COMMANDS ────────────────────────────────────────────────────────

cmd_quarantine_list() {
    header; echo -e "\n  ${BOLD}Quarantined Domains:${NC}\n"
    py "
import json,time
try: data=json.load(open('$QUARANTINE'))
except: data={}
if not data: print('  No domains in quarantine.'); exit()
print(f\"  {'Domain':<30} {'Since':<18} {'Trigger':<10} {'Reason'}\")
print(f\"  {'─'*30} {'─'*18} {'─'*10} {'─'*20}\")
for domain,i in data.items():
    since=time.strftime('%Y-%m-%d %H:%M',time.localtime(i.get('time',0)))
    trigger=str(i.get('trigger_count','?'))
    reason=i.get('reason','?')
    print(f'  {domain:<30} {since:<18} {trigger:<10} {reason}')
print()
"
}

cmd_quarantine_on() {
    [ -z "$1" ] && echo -e "${RED}Usage: vg quarantine-on <domain>${NC}" && exit 1
    local domain="$1"
    echo -e "${YELLOW}Enabling quarantine for: $domain${NC}"
    python3 - "$domain" <<PYEOF
import sys
$QUARANTINE_PY_HELPERS
domain = sys.argv[1]
result = q_on(domain, reason="manual")
if result:
    print("\033[0;32m  ✅ Quarantine ON: " + domain + "\033[0m")
    print("  htaccess updated + challenge PHP deployed.")
else:
    sys.exit(1)
PYEOF
}

cmd_quarantine_off() {
    [ -z "$1" ] && echo -e "${RED}Usage: vg quarantine-off <domain>${NC}" && exit 1
    local domain="$1"
    echo -e "${YELLOW}Releasing quarantine for: $domain${NC}"
    python3 - "$domain" <<PYEOF
import sys
$QUARANTINE_PY_HELPERS
domain = sys.argv[1]
result = q_off(domain)
if result:
    print("\033[0;32m  ✅ Quarantine OFF: " + domain + "\033[0m")
    print("  htaccess restored, challenge PHP removed.")
else:
    sys.exit(1)
PYEOF
}

cmd_quarantine_off_all() {
    echo -e "${YELLOW}Releasing ALL quarantined domains...${NC}"
    python3 - <<PYEOF
$QUARANTINE_PY_HELPERS
ql = _load(QFILE, {})
if not ql:
    print("  No domains in quarantine.")
    exit()
count = 0
for domain in list(ql.keys()):
    result = q_off(domain)
    if result:
        print("\033[0;32m  ✅ " + domain + "\033[0m")
        count += 1
    else:
        print("\033[0;31m  ✗ failed: " + domain + "\033[0m")
print("\n  Released " + str(count) + " domain(s).")
PYEOF
    echo -e "${GREEN}Done.${NC}"
}

# ── LOG ────────────────────────────────────────────────────────────────────────

cmd_log()       { echo -e "${CYAN}━━━━ Live Log (Ctrl+C to exit) ━━━━${NC}"; tail -f "$LOGFILE"; }
cmd_log_clear() { > "$LOGFILE"; echo -e "${GREEN}✅ Log cleared.${NC}"; }

# ── SERVICE ────────────────────────────────────────────────────────────────────

cmd_start()   { systemctl start   ddos-guard && echo -e "${GREEN}▶ Started.${NC}"; }
cmd_stop()    { systemctl stop    ddos-guard && echo -e "${YELLOW}⏹ Stopped.${NC}"; }
cmd_restart() { systemctl restart ddos-guard && echo -e "${GREEN}↺ Restarted.${NC}"; }

# ── HELP ───────────────────────────────────────────────────────────────────────

cmd_help() {
    header; echo ""
    echo -e "  ${BOLD}INFO${NC}"
    echo -e "  vg status                         Service status + config"
    echo -e "  vg log                            Live log"
    echo -e "  vg log-clear                      Clear log file"
    echo ""
    echo -e "  ${BOLD}CONFIG${NC}"
    echo -e "  vg config                         Show all config"
    echo -e "  vg config <key> <value>           Edit config"
    echo -e "    IP Block: window_seconds, block_threshold, perm_threshold, check_interval"
    echo -e "    Quarantine: quarantine_single_ip, quarantine_all_ip"
    echo ""
    echo -e "  ${BOLD}QUARANTINE${NC}"
    echo -e "  vg quarantine                     Show quarantined domains"
    echo -e "  vg quarantine-on <domain>         Manually quarantine a domain"
    echo -e "  vg quarantine-off <domain>        Release a domain from quarantine"
    echo -e "  vg quarantine-off-all             Release ALL quarantined domains"
    echo ""
    echo -e "  ${BOLD}BLOCKLIST${NC}"
    echo -e "  vg blocklist                      Show all blocked IPs"
    echo -e "  vg blocklist-temp                 Show 7-day blocked only"
    echo -e "  vg blocklist-perm                 Show permanent blocked only"
    echo -e "  vg blocklist-add <ip> [ip2]       Add IP(s) — permanent"
    echo -e "  vg blocklist-add-temp <ip> [ip2]  Add IP(s) — 7 days"
    echo -e "  vg blocklist-remove <ip> [ip2]    Remove specific IP(s)"
    echo -e "  vg blocklist-remove-all           Remove ALL blocked IPs"
    echo -e "  vg blocklist-remove-all-temp      Remove all 7-day blocks"
    echo -e "  vg blocklist-remove-all-perm      Remove all permanent blocks"
    echo ""
    echo -e "  ${BOLD}WHITELIST${NC}"
    echo -e "  vg whitelist                      Show whitelisted IPs"
    echo -e "  vg whitelist-add <ip> [ip2]       Add IP(s)"
    echo -e "  vg whitelist-remove <ip> [ip2]    Remove IP(s)"
    echo -e "  vg whitelist-remove-all           Remove all"
    echo ""
    echo -e "  ${BOLD}SERVICE${NC}"
    echo -e "  vg start / vg stop / vg restart"
    echo ""
}

# ── ROUTER ─────────────────────────────────────────────────────────────────────

case "$1" in
    status)                    cmd_status ;;
    config)                    cmd_config "$2" "$3" ;;
    log)                       cmd_log ;;
    log-clear)                 cmd_log_clear ;;
    quarantine)                cmd_quarantine_list ;;
    quarantine-on)             cmd_quarantine_on "$2" ;;
    quarantine-off)            cmd_quarantine_off "$2" ;;
    quarantine-off-all)        cmd_quarantine_off_all ;;
    blocklist)                 cmd_blocklist ;;
    blocklist-temp)            cmd_blocklist_temp ;;
    blocklist-perm)            cmd_blocklist_perm ;;
    blocklist-add)             shift; cmd_blocklist_add "$@" ;;
    blocklist-add-temp)        shift; cmd_blocklist_add_temp "$@" ;;
    blocklist-remove)          shift; cmd_blocklist_remove "$@" ;;
    blocklist-remove-all)      cmd_blocklist_remove_all ;;
    blocklist-remove-all-temp) cmd_blocklist_remove_all_temp ;;
    blocklist-remove-all-perm) cmd_blocklist_remove_all_perm ;;
    whitelist)                 cmd_whitelist ;;
    whitelist-add)             shift; cmd_whitelist_add "$@" ;;
    whitelist-remove)          shift; cmd_whitelist_remove "$@" ;;
    whitelist-remove-all)      cmd_whitelist_remove_all ;;
    start)                     cmd_start ;;
    stop)                      cmd_stop ;;
    restart)                   cmd_restart ;;
    help|--help|-h)            cmd_help ;;
    *)                         cmd_help ;;
esac
