#!/usr/bin/env bash
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# SecuBox-Deb :: autheliactl
# CyberMind — https://cybermind.fr
#
# Authelia host-side controller. LXC at 10.100.0.20 on br-lxc.
# Three-fold introspection (components/status/access) + dashboard, datasource,
# alert, user, api-key, install, reload verbs.
#
# Grammar: docs/grammar.md, OPS MONITORING layer.
# Conventions: docs/MODULE-GUIDELINES.md §7.

set -u

readonly VERSION="1.0.0"
readonly CONFIG_FILE="${SECUBOX_GRAFANA_CONFIG:-/etc/secubox/authelia.toml}"
readonly STATE_DIR="${SECUBOX_GRAFANA_STATE:-/var/lib/secubox/authelia}"
readonly SECRETS_DIR="${SECUBOX_SECRETS_DIR:-/etc/secubox/secrets}"
readonly INSTALL_LIB="${SECUBOX_INSTALL_LIB:-/usr/share/secubox/lib/authelia/install-lxc.sh}"

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

log()   { printf '%b[authelia]%b %s\n' "$GREEN" "$NC" "$*"; }
warn()  { printf '%b[warn]%b %s\n'    "$YELLOW" "$NC" "$*" >&2; }
err()   { printf '%b[error]%b %s\n'   "$RED"    "$NC" "$*" >&2; }

# ── TOML config (best-effort, grep-style — same as giteactl) ─────────────────
config_get() {
    # Strips TOML inline `# comment`, surrounding whitespace, and quotes.
    # Previous version concatenated `# comment text` into the value
    # (eg HTTP_PORT became "9091  # comment"), breaking URL construction.
    local key="$1" default="${2:-}" raw=""
    if [ -f "$CONFIG_FILE" ]; then
        raw=$(grep -E "^[[:space:]]*${key}[[:space:]]*=" "$CONFIG_FILE" 2>/dev/null | head -1)
    fi
    if [ -z "$raw" ]; then
        echo "$default"
        return
    fi
    local val
    val=$(echo "$raw" | cut -d= -f2- | sed 's/[[:space:]]*#.*$//' \
        | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' \
        | tr -d '"' | tr -d "'")
    if [ -z "$val" ]; then
        echo "$default"
    else
        echo "$val"
    fi
}

LXC_NAME=$(config_get "name" "authelia")
LXC_IP=$(config_get "ip" "10.100.0.20")
LXC_PATH=$(config_get "path" "/data/lxc")
HTTP_PORT=$(config_get "http_port" "9091")
PUBLIC_HOSTNAME=$(config_get "public_hostname" "sso.gk2.secubox.in")

# ── LXC helpers ───────────────────────────────────────────────────────────────
lxc_state() {
    lxc-info -n "$LXC_NAME" -P "$LXC_PATH" 2>/dev/null \
        | awk -F: '/^State:/ { gsub(/ /,"",$2); print tolower($2) }'
}

lxc_running() { [ "$(lxc_state)" = "running" ]; }
lxc_exists()  { [ -d "$LXC_PATH/$LXC_NAME/rootfs" ]; }

daemon_running() {
    lxc_running || return 1
    lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl is-active --quiet authelia 2>/dev/null
}

host_api_running() {
    systemctl is-active --quiet secubox-authelia.service 2>/dev/null
}

# ── Three-fold output (matches docs/MODULE-GUIDELINES.md §7) ──────────────────
emit_components_json() {
    local lxc_st daemon_st api_st
    if lxc_exists; then
        lxc_st=$(lxc_state)
        [ -z "$lxc_st" ] && lxc_st="absent"
    else
        lxc_st="absent"
    fi
    daemon_running && daemon_st="running" || daemon_st="stopped"
    host_api_running && api_st="running" || api_st="stopped"

    cat <<EOF
{
  "module": "authelia",
  "version": "$VERSION",
  "components": [
    {"name": "lxc",      "state": "$lxc_st",      "detail": "$LXC_NAME @ $LXC_IP on br-lxc"},
    {"name": "daemon",   "state": "$daemon_st",   "detail": "authelia, port $HTTP_PORT"},
    {"name": "host-api", "state": "$api_st",      "detail": "secubox-authelia.service (uvicorn @ /run/secubox/authelia.sock)"}
  ]
}
EOF
}

emit_components_text() {
    printf '%-12s %-12s %s\n' "COMPONENT" "STATE" "DETAIL"
    if lxc_exists; then
        local s; s=$(lxc_state); [ -z "$s" ] && s="absent"
        printf '%-12s %-12s %s\n' "lxc" "$s" "$LXC_NAME @ $LXC_IP on br-lxc"
    else
        printf '%-12s %-12s %s\n' "lxc" "absent" "$LXC_NAME @ $LXC_IP on br-lxc — run 'autheliactl install'"
    fi
    if daemon_running; then
        printf '%-12s %-12s %s\n' "daemon" "running" "authelia, port $HTTP_PORT"
    else
        printf '%-12s %-12s %s\n' "daemon" "stopped" "authelia, port $HTTP_PORT"
    fi
    if host_api_running; then
        printf '%-12s %-12s %s\n' "host-api" "running" "secubox-authelia.service (uvicorn)"
    else
        printf '%-12s %-12s %s\n' "host-api" "stopped" "secubox-authelia.service (uvicorn)"
    fi
}

emit_status_json() {
    local overall
    if lxc_running && daemon_running && host_api_running; then
        overall="green"
    elif lxc_exists && (daemon_running || host_api_running); then
        overall="yellow"
    else
        overall="red"
    fi
    cat <<EOF
{
  "module": "authelia",
  "version": "$VERSION",
  "overall": "$overall"
}
EOF
}

emit_access_json() {
    # Real Authelia login portal URLs — NOT the /authelia/ admin subpath.
    # The admin webui's "Open SSO Portal" button reads this; consistency
    # comes from one source.
    #   lan:    http://<lxc_ip>:9091/  direct to Authelia daemon (LAN-only)
    #   public: https://<PUBLIC_HOSTNAME>/  dedicated vhost
    local lan_url public_url
    lan_url="http://${LXC_IP}:${HTTP_PORT}/"
    if [ -n "$PUBLIC_HOSTNAME" ]; then
        public_url="https://${PUBLIC_HOSTNAME}/"
    else
        public_url=""
    fi
    cat <<EOF
{
  "module": "authelia",
  "access": [
    {"url": "$lan_url",    "auth": "lan-only",     "scope": "lan"}$( [ -n "$public_url" ] && echo ',' )
    $( [ -n "$public_url" ] && cat <<PUB
{"url": "$public_url", "auth": "Authelia SSO", "scope": "public"}
PUB
)
  ]
}
EOF
}

# ── Verbs ─────────────────────────────────────────────────────────────────────
cmd_components() {
    case "${1:-}" in
        --json) emit_components_json ;;
        ""|list|status) emit_components_text ;;
        *) err "unknown verb for 'components': $1"; exit 1 ;;
    esac
}

cmd_status() {
    case "${1:-}" in
        --json) emit_status_json ;;
        ""|list) emit_components_text ; echo ; emit_status_json | python3 -c 'import json,sys; d=json.load(sys.stdin); print("overall:", d["overall"])' ;;
        *) err "unknown verb for 'status': $1"; exit 1 ;;
    esac
}

cmd_access() {
    case "${1:-}" in
        --json) emit_access_json ;;
        ""|list) emit_access_json | python3 -m json.tool ;;
        show)
            local name="${2:-lan}"
            emit_access_json | python3 -c "import json,sys; d=json.load(sys.stdin); m=[a for a in d['access'] if a['scope']=='$name']; print(m[0] if m else 'not found')"
            ;;
        *) err "unknown verb for 'access': $1"; exit 1 ;;
    esac
}

cmd_install() {
    if [ ! -x "$INSTALL_LIB" ]; then
        err "install script not found at $INSTALL_LIB (package not installed correctly?)"
        exit 2
    fi
    log "Running LXC bootstrap from $INSTALL_LIB ..."
    SECUBOX_LXC_NAME="$LXC_NAME" SECUBOX_LXC_IP="$LXC_IP" SECUBOX_LXC_PATH="$LXC_PATH" \
        bash "$INSTALL_LIB"
    log "Starting host-side FastAPI ..."
    systemctl start secubox-authelia.service 2>/dev/null || true
    log "Done. Try 'autheliactl status' to verify."
}

cmd_reload() {
    systemctl restart secubox-authelia.service 2>/dev/null || true
    if lxc_running; then
        lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl restart authelia-server 2>/dev/null || true
    fi
    log "Reloaded host FastAPI and authelia-server (if running)."
}

cmd_help() {
    cat <<'HELP'
autheliactl — SecuBox Authelia SSO IdP controller (AUTH-BRIDGE layer)

Three-fold introspection:
  autheliactl components [--json]      # LXC + authelia daemon + host-API states
  autheliactl status     [--json]      # overall green/yellow/red
  autheliactl access     [list|show <name>] [--json]

Lifecycle (per docs/MODULE-GUIDELINES.md §7):
  autheliactl install                  # idempotent LXC + Authelia bootstrap
  autheliactl reload                   # restart host FastAPI + authelia daemon
  autheliactl repair                   # detect + fix drift (NOT YET IMPLEMENTED)
  autheliactl wizard                   # interactive seed + install (NOT YET IMPLEMENTED)
  autheliactl uninstall                # remove LXC + state + secrets (NOT YET IMPLEMENTED)

SSO nouns:
  autheliactl provider     list|add <name>|remove <name>|test <name>
  autheliactl oidc-client  list|add <toml>|remove <id>|rotate-secret <id>
  autheliactl user         list|enable <name>|disable <name>|enrol-totp <name>
  autheliactl session      list|kill <id>|killall <user>
  autheliactl totp         list|reset <user>|verify <user> <code>

  (--json on any verb returns machine-readable output)

For grammar details: /usr/share/doc/secubox-authelia/README.gz
                     docs/grammar.md (AUTH-BRIDGE layer, #239)
HELP
}

# ── Dispatch ──────────────────────────────────────────────────────────────────
main() {
    local noun="${1:-help}"
    shift || true
    case "$noun" in
        components|status|access|install|reload) "cmd_${noun}" "$@" ;;
        # The remaining nouns are stubs in v1.0.0 — see #230 task G5 for
        # full implementation. They print a clear "not implemented yet" so
        # the help/grammar surface is consistent now and the API contract
        # is testable.
        provider|user|session|totp|oidc-client)
            err "noun '$noun' not yet implemented in v$VERSION — see #239 follow-up"
            exit 2
            ;;
        --help|-h|help|"") cmd_help ;;
        --version|-V) echo "autheliactl $VERSION" ;;
        *) err "unknown noun: $noun"; cmd_help; exit 1 ;;
    esac
}

main "$@"
