#!/bin/bash
set -e

PORT=9105
HOST="localhost"
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[0;33m'; NC='\033[0m'

command_exists() { command -v "$1" >/dev/null 2>&1; }

# --- XDG PATH RESOLUTION ---
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do
    DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
    SOURCE="$(readlink "$SOURCE")"
    [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
export SCRIPT_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

export PASSEPARTOUT_CONFIG_DIR="$(realpath -m "${XDG_CONFIG_HOME:-$HOME/.config}/passepartout")"
export PASSEPARTOUT_DATA_DIR="$(realpath -m "${XDG_DATA_HOME:-$HOME/.local/share}/passepartout")"
export PASSEPARTOUT_STATE_DIR="$(realpath -m "${XDG_STATE_HOME:-$HOME/.local/state}/passepartout")"
export PASSEPARTOUT_BIN_DIR="$(realpath -m "${XDG_BIN_HOME:-$HOME/.local/bin}")"
export PASSEPARTOUT_MEMEX_DIR="${PASSEPARTOUT_MEMEX_DIR:-$HOME/memex}"

if [ -f "$PASSEPARTOUT_CONFIG_DIR/.env" ]; then
    set -a; source "$PASSEPARTOUT_CONFIG_DIR/.env"; set +a
fi

# --- DISTRO DETECTION ---
detect_distro() {
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        case "$ID" in
            debian|ubuntu|linuxmint|pop|elementary|zorin) echo "debian" ;;
            fedora|rhel|centos|rocky|almalinux) echo "fedora" ;;
            *) echo "unknown" ;;
        esac
    elif command_exists apt-get; then echo "debian"
    elif command_exists dnf; then echo "fedora"
    else echo "unknown"; fi
}

distro_install() {
    local distro=$(detect_distro); shift
    case "$distro" in
        debian) sudo apt-get update && sudo apt-get install -y "$@" ;;
        fedora) sudo dnf install -y "$@" ;;
        *) echo "Unsupported distro. Install manually: sbcl emacs git curl socat"; return 1 ;;
    esac
}

# --- DEPENDENCY CHECK ---
check_dependencies() {
    local missing=()
    for dep in sbcl git curl socat nc; do
        if ! command_exists "$dep"; then missing+=("$dep"); fi
    done
    if ! command_exists emacs; then missing+=("emacs-nox"); fi
    if [ ${#missing[@]} -gt 0 ]; then
        echo -e "${YELLOW}--- Installing missing dependencies: ${missing[*]} ---${NC}"
        local distro=$(detect_distro)
        case "$distro" in
            debian)
                distro_install "${missing[@]}" libssl-dev libncurses-dev libffi-dev zlib1g-dev libsqlite3-dev 2>/dev/null || true
                if ! command_exists rlwrap; then distro_install rlwrap 2>/dev/null || true; fi
                if ! command_exists nc; then distro_install netcat-openbsd 2>/dev/null || true; fi
                ;;
            fedora)
                distro_install "${missing[@]}" openssl-devel ncurses-devel libffi-devel zlib-devel sqlite-devel 2>/dev/null || true
                if ! command_exists rlwrap; then distro_install rlwrap 2>/dev/null || true; fi
                if ! command_exists nc; then distro_install nmap-ncat 2>/dev/null || true; fi
                ;;
        esac
    fi
}

# --- SETUP ---
setup_system() {
    NON_INTERACTIVE=false; WITH_FIREWALL=false
    for arg in "$@"; do
        case "$arg" in
            --non-interactive) NON_INTERACTIVE=true ;;
            --with-firewall) WITH_FIREWALL=true ;;
        esac
    done

    echo -e "${BLUE}=== Passepartout: Configure ===${NC}"
    mkdir -p "$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" "$PASSEPARTOUT_STATE_DIR" "$PASSEPARTOUT_BIN_DIR"
    mkdir -p "$PASSEPARTOUT_DATA_DIR/harness" "$PASSEPARTOUT_DATA_DIR/tests" "$PASSEPARTOUT_DATA_DIR/skills"

    check_dependencies

    if [ ! -d "$HOME/quicklisp" ]; then
        echo -e "${YELLOW}--- Installing Quicklisp ---${NC}"
        curl -O https://beta.quicklisp.org/quicklisp.lisp
        sbcl --non-interactive --load quicklisp.lisp \
             --eval "(quicklisp-quickstart:install)" \
             --eval "(ql-util:without-prompting (ql:add-to-init-file))"
        rm quicklisp.lisp
    fi

    echo -e "${YELLOW}--- Deploying Engine to $PASSEPARTOUT_DATA_DIR ---${NC}"
    cp "$SCRIPT_DIR/passepartout.asd" "$PASSEPARTOUT_DATA_DIR/"
    mkdir -p "$PASSEPARTOUT_DATA_DIR/harness" "$PASSEPARTOUT_DATA_DIR/tests" "$PASSEPARTOUT_DATA_DIR/skills"
    export INSTALL_DIR="$PASSEPARTOUT_DATA_DIR"

    cp "$SCRIPT_DIR/org"/*.org "$PASSEPARTOUT_DATA_DIR/harness/"
    (cd "$PASSEPARTOUT_DATA_DIR/harness" && emacs -Q --batch \
        --eval "(require 'org)" \
        --eval "(setq org-confirm-babel-evaluate nil)" \
        --eval "(org-babel-tangle-file \"manifest.org\")") >/dev/null 2>&1 || true
    for f in "$PASSEPARTOUT_DATA_DIR/harness"/*.org; do
        fname=$(basename "$f" .org)
        [ "$fname" = "manifest" ] && continue
        echo "Tangling harness/$fname.org..."
        (cd "$PASSEPARTOUT_DATA_DIR/harness" && emacs -Q --batch \
            --eval "(require 'org)" \
            --eval "(setq org-confirm-babel-evaluate nil)" \
            --eval "(org-babel-tangle-file \"${fname}.org\")") >/dev/null 2>&1 || true
    done
    find "$PASSEPARTOUT_DATA_DIR/harness" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 2>/dev/null || true
    rm -f "$PASSEPARTOUT_DATA_DIR/harness"/*.org

    for f in "$SCRIPT_DIR/org"/*.org; do
        fname=$(basename "$f" .org)
        echo "Tangling skills/$fname.org..."
        cp "$f" "$PASSEPARTOUT_DATA_DIR/skills/"
        (cd "$PASSEPARTOUT_DATA_DIR/skills" && emacs -Q --batch \
            --eval "(require 'org)" \
            --eval "(setq org-confirm-babel-evaluate nil)" \
            --eval "(org-babel-tangle-file \"${fname}.org\")") >/dev/null 2>&1 || true
        rm -f "$PASSEPARTOUT_DATA_DIR/skills/$fname.org"
    done
    find "$PASSEPARTOUT_DATA_DIR/skills" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 2>/dev/null || true
    [ -f "$PASSEPARTOUT_DATA_DIR/run-all-tests.lisp" ] && mv "$PASSEPARTOUT_DATA_DIR/run-all-tests.lisp" "$PASSEPARTOUT_DATA_DIR/harness/"
    rm -f "$PASSEPARTOUT_DATA_DIR/harness"/*.org "$PASSEPARTOUT_DATA_DIR/skills"/*.org

    ln -sf "$SCRIPT_DIR/passepartout.sh" "$PASSEPARTOUT_BIN_DIR/passepartout"

    if [ "$WITH_FIREWALL" = true ]; then
        case $(detect_distro) in
            debian) sudo ufw allow 9105/tcp 2>/dev/null && echo "✓ UFW: port 9105 opened" || true ;;
            fedora) sudo firewall-cmd --add-port=9105/tcp --permanent 2>/dev/null && sudo firewall-cmd --reload 2>/dev/null && echo "✓ firewalld: port 9105 opened" || true ;;
        esac
    fi

    if [ "$NON_INTERACTIVE" = true ]; then
        echo "Configure complete."
        exit 0
    fi

    echo -e "${YELLOW}--- Launching Setup Wizard ---${NC}"
    exec sbcl --non-interactive \
        --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
        --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
        --eval '(ql:quickload :passepartout :force t)' \
        --eval '(passepartout:skill-initialize-all)' \
        --eval '(funcall (find-symbol "SETUP-WIZARD-RUN" :passepartout))'
}

# --- DOCTOR REPAIR ---
doctor_repair() {
    echo -e "${BLUE}=== Passepartout: Repair Mode ===${NC}"
    check_dependencies
    mkdir -p "$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" "$PASSEPARTOUT_STATE_DIR" "$PASSEPARTOUT_BIN_DIR"
    mkdir -p "$PASSEPARTOUT_DATA_DIR/harness" "$PASSEPARTOUT_DATA_DIR/tests" "$PASSEPARTOUT_DATA_DIR/skills"
    for f in "$SCRIPT_DIR/org"/*.org; do
        [ -f "$f" ] || continue
        fname=$(basename "$f" .org)
        echo "  Checking harness/$fname..."
        if ! sbcl --non-interactive \
            --eval "(load \"$PASSEPARTOUT_DATA_DIR/harness/${fname}.lisp\")" \
            --eval "(format t \"OK~%\")" 2>/dev/null | grep -q "OK"; then
            echo "    Re-tangling $fname.org..."
            (cd "$PASSEPARTOUT_DATA_DIR/harness" && emacs -Q --batch \
                --eval "(require 'org)" \
                --eval "(setq org-confirm-babel-evaluate nil)" \
                --eval "(org-babel-tangle-file \"$f\")") >/dev/null 2>&1 || true
        fi
    done
    for f in "$SCRIPT_DIR/org"/*.org; do
        [ -f "$f" ] || continue
        fname=$(basename "$f" .org)
        echo "  Checking skill/$fname..."
        if ! sbcl --non-interactive \
            --eval "(load \"$PASSEPARTOUT_DATA_DIR/skills/${fname}.lisp\")" \
            --eval "(format t \"OK~%\")" 2>/dev/null | grep -q "OK"; then
            echo "    Re-tangling $fname.org..."
            cp "$f" "$PASSEPARTOUT_DATA_DIR/skills/"
            (cd "$PASSEPARTOUT_DATA_DIR/skills" && emacs -Q --batch \
                --eval "(require 'org)" \
                --eval "(setq org-confirm-babel-evaluate nil)" \
                --eval "(org-babel-tangle-file \"${fname}.org\")") >/dev/null 2>&1 || true
            rm -f "$PASSEPARTOUT_DATA_DIR/skills/$fname.org"
        fi
    done
    rm -f "$PASSEPARTOUT_DATA_DIR/harness"/*.org "$PASSEPARTOUT_DATA_DIR/skills"/*.org 2>/dev/null || true
    echo -e "${GREEN}--- Repair Complete ---${NC}"
}

# --- INSTALL SKILL ---
install_skill() {
    local SKILL_NAME=$1
    if [ -z "$SKILL_NAME" ]; then
        echo "Usage: passepartout install skill <skill-name>"
        echo "  Installs a skill from passepartout-contrib"
        echo ""
        echo "Available skills:"
        if [ -d "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills" ]; then
            ls "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills"/*.org 2>/dev/null | xargs -I{} basename {} .org | sed 's/org-skill-//' | sort | uniq
        else
            echo "  (clone passepartout-contrib to ~/memex/projects/ first)"
        fi
        exit 1
    fi
    local SKILL_FILE="org-skill-${SKILL_NAME}.org"
    local SOURCE_DIR="$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib/skills"
    local TARGET_DIR="$PASSEPARTOUT_DATA_DIR/skills"
    if [ ! -d "$SOURCE_DIR" ]; then
        echo "Error: Contrib skills not found at $SOURCE_DIR"
        echo "Run: git clone https://github.com/amrgharbeia/passepartout-contrib.git \$PASSEPARTOUT_MEMEX_DIR/projects/passepartout-contrib"
        exit 1
    fi
    if [ ! -f "$SOURCE_DIR/$SKILL_FILE" ]; then
        echo "Error: Skill '$SKILL_NAME' not found"
        exit 1
    fi
    mkdir -p "$TARGET_DIR"
    cp "$SOURCE_DIR/$SKILL_FILE" "$TARGET_DIR/"
    (cd "$TARGET_DIR" && emacs -Q --batch \
        --eval "(require 'org)" \
        --eval "(setq org-confirm-babel-evaluate nil)" \
        --eval "(org-babel-tangle-file \"$SKILL_FILE\")") >/dev/null 2>&1 || true
    rm -f "$TARGET_DIR/$SKILL_FILE"
    if [ -f "$TARGET_DIR/${SKILL_NAME}-tests.lisp" ]; then
        mv "$TARGET_DIR/${SKILL_NAME}-tests.lisp" "$PASSEPARTOUT_DATA_DIR/tests/" 2>/dev/null || true
    fi
    echo "Skill '$SKILL_NAME' installed. Restart to activate."
}

# --- INSTALL SERVICE ---
install_service() {
    mkdir -p "$HOME/.config/systemd/user"
    cat > "$HOME/.config/systemd/user/passepartout.service" << 'SERVICEEOF'
[Unit]
Description=Passepartout Daemon
After=network.target

[Service]
Type=simple
ExecStart=%h/projects/passepartout/passepartout.sh daemon
Restart=on-failure
RestartSec=10
WorkingDirectory=%h/projects/passepartout

[Install]
WantedBy=default.target
SERVICEEOF
    systemctl --user daemon-reload
    systemctl --user enable passepartout.service
    systemctl --user start passepartout.service
    echo -e "${GREEN}✓ passepartout.service installed and started${NC}"
    echo "  Status: systemctl --user status passepartout.service"
    echo "  Logs:   journalctl --user -u passepartout.service -f"
}

uninstall_service() {
    systemctl --user stop passepartout.service 2>/dev/null || true
    systemctl --user disable passepartout.service 2>/dev/null || true
    rm -f "$HOME/.config/systemd/user/passepartout.service"
    systemctl --user daemon-reload
    echo -e "${GREEN}✓ passepartout.service removed${NC}"
}

# --- BACKUP ---
backup() {
    local dest="${1:-$HOME/passepartout-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
    if [ -f "$dest" ]; then echo "Error: $dest exists"; exit 1; fi
    echo "Backing up to $dest..."
    tar -czf "$dest" \
        "$PASSEPARTOUT_CONFIG_DIR" "$PASSEPARTOUT_DATA_DIR" \
        "$PASSEPARTOUT_MEMEX_DIR/gtd.org" "$PASSEPARTOUT_MEMEX_DIR/projects/passepartout" \
        2>/dev/null || true
    echo -e "${GREEN}✓ Backed up to $dest${NC}"
}

restore() {
    local src="$1"
    if [ -z "$src" ] || [ ! -f "$src" ]; then
        echo "Usage: passepartout restore <backup-file>"
        exit 1
    fi
    echo "Restoring from $src..."
    tar -xzf "$src" -C /
    echo -e "${GREEN}✓ Restored. Run 'passepartout doctor' to verify.${NC}"
}

# --- HELP ---
help() {
    echo ""
    echo "Passepartout — Your Autonomous, Plain-Text Life Assistant"
    echo ""
    echo "Usage: passepartout.sh <command> [options]"
    echo ""
    echo "System:"
    echo "  configure [--non-interactive] [--with-firewall]    Install or reconfigure the system"
    echo "  setup                                               Alias for configure"
    echo "  doctor [--fix] [--watch]                            System health check"
    echo ""
    echo "Running:"
    echo "  daemon                                              Start background daemon"
    echo "  tui                                                 Launch terminal UI"
    echo "  gateway {link|unlink|list} <platform> <token>       Manage chat gateways"
    echo ""
    echo "Skills:"
    echo "  install skill <name>                                Install a skill from contrib"
    echo "  install service                                     Install systemd service (auto-start)"
    echo "  uninstall service                                   Remove systemd service"
    echo ""
    echo "Data:"
    echo "  backup [path]                                       Backup config, data, memex"
    echo "  restore <path>                                      Restore from a backup"
    echo ""
    echo "Quick start:"
    echo "  curl -fsSL https://raw.githubusercontent.com/amrgharbeia/passepartout/main/passepartout.sh | bash -s configure"
    echo ""
}

# --- COMMAND ROUTER ---
COMMAND=$1; [ -z "$COMMAND" ] && COMMAND="help"
shift || true

case "$COMMAND" in
    configure|setup)
        check_dependencies
        if [ "$1" = "--add-provider" ]; then
            sbcl --non-interactive \
                --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                --eval '(ql:quickload :passepartout :force t)' \
                --eval '(passepartout:skill-initialize-all)' \
                --eval '(funcall (find-symbol "SETUP-PROVIDER-ADD" :passepartout))'
        elif [ "$1" = "--link" ]; then
            exec "$0" gateway link "$2" "$3"
        else
            setup_system "$@"
        fi
        ;;
    doctor)
        check_dependencies
        if [ "$1" = "--watch" ]; then
            while true; do
                echo "--- $(date '+%Y-%m-%d %H:%M:%S') ---"
                sbcl --non-interactive \
                    --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                    --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                    --eval '(ql:quickload :passepartout :force t)' \
                    --eval '(passepartout:skill-initialize-all)' \
                    --eval '(funcall (find-symbol "DIAGNOSTICS-RUN-ALL" :passepartout))' 2>&1 | grep -E "(HEALTH|OK|FAIL|WARN|SYSTEM|===)" || true
                sleep 60
            done
        elif [ "$1" = "--fix" ]; then
            if [ ! -f "$PASSEPARTOUT_DATA_DIR/harness/package.lisp" ] || [ ! -f "$PASSEPARTOUT_DATA_DIR/harness/skills.lisp" ]; then
                setup_system "$@"
            else
                doctor_repair
            fi
        else
            exec sbcl --non-interactive \
                --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                --eval '(ql:quickload :passepartout :force t)' \
                --eval '(passepartout:skill-initialize-all)' \
                --eval '(funcall (find-symbol "DIAGNOSTICS-MAIN" :passepartout))'
        fi
        ;;
    daemon)
        check_dependencies
        echo "Starting daemon in background..."
        nohup sbcl --non-interactive \
            --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
            --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
            --eval "(ql:quickload '(:passepartout :croatoan))" \
            --eval '(passepartout:main)' \
            > "$PASSEPARTOUT_STATE_DIR/daemon.log" 2>&1 &
        echo "Waiting for port 9105..."
        for i in $(seq 1 20); do
            if ss -tln 2>/dev/null | grep -q 9105 || netstat -tln 2>/dev/null | grep -q 9105; then
                echo "✓ Daemon ready on port 9105"; exit 0
            fi
            sleep 1
        done
        echo "✗ Daemon failed to start. Check $PASSEPARTOUT_STATE_DIR/daemon.log"; exit 1
        ;;
    tui)
        check_dependencies
        if ! ss -tln 2>/dev/null | grep -q 9105 && ! netstat -tln 2>/dev/null | grep -q 9105; then
            echo "Starting daemon first..."
            $0 daemon
        fi
        sbcl \
            --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
            --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
            --eval '(ql:quickload :passepartout/tui)' \
            --eval '(passepartout.tui:main)' || {
            echo "TUI error. Run 'passepartout doctor --fix'"; exit 1
        }
        ;;
    gateway)
        SUBCMD=$1; PLATFORM=$2; TOKEN=$3
        check_dependencies
        case "$SUBCMD" in
            list)
                exec sbcl --non-interactive \
                    --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                    --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                    --eval '(ql:quickload :passepartout :force t)' \
                    --eval '(passepartout:skill-initialize-all)' \
                    --eval '(funcall (find-symbol "GATEWAY-LIST-PRINT" (find-package "OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MANAGER")))'
                ;;
            link)
                [ -z "$PLATFORM" ] || [ -z "$TOKEN" ] && echo "Usage: passepartout gateway link <platform> <token>" && exit 1
                exec sbcl --non-interactive \
                    --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                    --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                    --eval '(ql:quickload :passepartout :force t)' \
                    --eval '(passepartout:skill-initialize-all)' \
                    --eval "(funcall (find-symbol \"GATEWAY-LINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MANAGER\")) \"$PLATFORM\" \"$TOKEN\")"
                ;;
            unlink)
                [ -z "$PLATFORM" ] && echo "Usage: passepartout gateway unlink <platform>" && exit 1
                exec sbcl --non-interactive \
                    --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
                    --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
                    --eval '(ql:quickload :passepartout :force t)' \
                    --eval '(passepartout:skill-initialize-all)' \
                    --eval "(funcall (find-symbol \"GATEWAY-UNLINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MANAGER\")) \"$PLATFORM\")"
                ;;
            *) echo "Usage: passepartout gateway {list|link|unlink}"; exit 1 ;;
        esac
        ;;
    install)
        case "$1" in
            skill) shift; install_skill "$@" ;;
            service) install_service ;;
            *) echo "Usage: passepartout install {skill|service}" >&2; exit 1 ;;
        esac
        ;;
    uninstall)
        case "$1" in
            service) uninstall_service ;;
            *) echo "Usage: passepartout uninstall {service}" >&2; exit 1 ;;
        esac
        ;;
    backup)
        backup "$1"
        ;;
    restore)
        restore "$1"
        ;;
    help|--help|-h)
        help
        ;;
    *)
        echo "Unknown command: $COMMAND"
        help
        exit 1
        ;;
esac
