#!/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="${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; 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)
                sudo apt-get update -qq 2>/dev/null || true
                distro_install "${missing[@]}" 2>/dev/null || true
                ;;
            fedora)
                distro_install "${missing[@]}" 2>/dev/null || true
                ;;
        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/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"

    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/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"
    export INSTALL_DIR="$PASSEPARTOUT_DATA_DIR"

    # Tangle all org files into lisp/
    for f in "$SCRIPT_DIR/org"/*.org; do
        [ -f "$f" ] || continue
        fname=$(basename "$f" .org)
        echo "Tangling $fname..."
        cp "$f" "$PASSEPARTOUT_DATA_DIR/org/"
        (cd "$PASSEPARTOUT_DATA_DIR/org" && 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
    # Move test files to tests/ directory
    find "$PASSEPARTOUT_DATA_DIR/lisp" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 2>/dev/null || true

    ln -sf "$SCRIPT_DIR/passepartout" "$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

    # Pre-compile core + TUI so first daemon/TUI start is fast
    echo -e "${YELLOW}--- Pre-compiling core system ---${NC}"
    sbcl --noinform --load "$HOME/quicklisp/setup.lisp" \
         --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
         --eval '(ql:quickload :passepartout)' \
         --eval '(ql:quickload :passepartout/tui :silent t)' \
         --eval '(uiop:quit)' 2>&1 | grep -v '^;' || true

    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/org" "$PASSEPARTOUT_DATA_DIR/lisp" "$PASSEPARTOUT_DATA_DIR/tests"
    for f in "$SCRIPT_DIR/org"/*.org; do
        [ -f "$f" ] || continue
        fname=$(basename "$f" .org)
        echo "  Checking $fname..."
        if ! sbcl --non-interactive \
            --eval "(load \"$PASSEPARTOUT_DATA_DIR/lisp/${fname}.lisp\")" \
            --eval "(format t \"OK~%\")" 2>/dev/null | grep -q "OK"; then
            echo "    Re-tangling $fname.org..."
            cp "$f" "$PASSEPARTOUT_DATA_DIR/org/"
            (cd "$PASSEPARTOUT_DATA_DIR/org" && 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
        fi
    done
    find "$PASSEPARTOUT_DATA_DIR/lisp" -name "*-tests.lisp" -exec mv {} "$PASSEPARTOUT_DATA_DIR/tests/" \; 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 <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
        # Use the script's directory as the data dir (development mode)
        # In production, set PASSEPARTOUT_DATA_DIR to your deployment path
        export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}"
        export MEMEX_DIR="${PASSEPARTOUT_MEMEX_DIR:-$HOME/memex}"
        echo "Starting daemon (data dir: $PASSEPARTOUT_DATA_DIR)..."
        nohup sbcl --non-interactive \
            --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
            --eval "(ql:quickload :passepartout)" \
            --eval "(load (format nil \"~alisp/system-model-router.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\")))" \
            --eval "(load (format nil \"~alisp/system-model-embedding.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\")))" \
            --eval "(load (format nil \"~alisp/system-model-explorer.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\")))" \
            --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
        export PASSEPARTOUT_DATA_DIR="${PASSEPARTOUT_DATA_DIR:-$SCRIPT_DIR}"
        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
        exec sbcl \
            --eval '(load (merge-pathnames "quicklisp/setup.lisp" (user-homedir-pathname)))' \
            --eval '(declaim (optimize (debug 3) (speed 0) (safety 3)))' \
            --eval "(push (truename \"$PASSEPARTOUT_DATA_DIR/\") asdf:*central-registry*)" \
            --eval '(ql:quickload :passepartout/tui)' \
            --eval '(in-package :passepartout)' \
            --eval "(load (format nil \"~alisp/system-model-provider.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\")))" \
            --eval "(load (format nil \"~alisp/system-model-explorer.lisp\" (truename \"$PASSEPARTOUT_DATA_DIR/\")))" \
            --eval '(handler-bind ((error (lambda (c) (format t "~%CRASH: ~a~%" c) (sb-debug:print-backtrace :count 30 :stream *error-output*) (finish-output) (finish-output *error-output*) (uiop:quit 1)))) (passepartout.gateway-tui:tui-main))'
        ;;
    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 "MESSAGING-LIST-PRINT" (find-package "OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING")))'
                ;;
            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 \"MESSAGING-LINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING\")) \"$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 \"MESSAGING-UNLINK\" (find-package \"OPENCORTEX.SKILLS.ORG-SKILL-GATEWAY-MESSAGING\")) \"$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
