#!/bin/bash
# =============================================================================
# Aegis - Interactive Production Installer
# =============================================================================
# Sets up the Aegis platform for production with an interactive wizard
# that configures all environment variables.
#
# Usage:
#   chmod +x scripts/install.sh
#   ./scripts/install.sh
#
# Prerequisites:
#   - Docker and Docker Compose installed
#   - Port 80 (or chosen port) available
# =============================================================================

set -e

# Always run from the project root (parent of scripts/)
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_ROOT"

# ── Colors & helpers ──────────────────────────────────────────────────────

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

print_ok()      { echo -e "${GREEN}[OK]${NC} $1"; }
print_warn()    { echo -e "${YELLOW}[!]${NC}  $1"; }
print_error()   { echo -e "${RED}[X]${NC}  $1"; }
print_info()    { echo -e "${CYAN}[i]${NC}  $1"; }
print_header()  { echo -e "\n${BOLD}── $1 ──${NC}\n"; }
print_prompt()  { echo -en "${CYAN}>>>${NC} $1"; }

# Generate a cryptographically secure random string
gen_secret() {
    python3 -c "import secrets; print(secrets.token_hex($1))" 2>/dev/null \
        || openssl rand -hex "$1" 2>/dev/null \
        || head -c "$1" /dev/urandom | od -An -tx1 | tr -d ' \n'
}

gen_password() {
    python3 -c "import secrets; print(secrets.token_urlsafe($1))" 2>/dev/null \
        || openssl rand -base64 "$1" 2>/dev/null \
        || head -c "$1" /dev/urandom | base64 | tr -d '=/+' | head -c "$1"
}

# ── Banner ────────────────────────────────────────────────────────────────

clear 2>/dev/null || true
echo ""
echo -e "${BOLD}"
echo "    ╔══════════════════════════════════════════════════════════╗"
echo "    ║                                                          ║"
echo "    ║              Aegis - Installation Wizard                 ║"
echo "    ║          MITRE ATT&CK Coverage Platform                  ║"
echo "    ║                                                          ║"
echo "    ╚══════════════════════════════════════════════════════════╝"
echo -e "${NC}"

# ═════════════════════════════════════════════════════════════════════════
# STEP 1: Check prerequisites
# ═════════════════════════════════════════════════════════════════════════

print_header "Step 1/5 - Checking prerequisites"

if ! command -v docker &> /dev/null; then
    print_error "Docker is not installed. Please install Docker first."
    echo "       https://docs.docker.com/engine/install/"
    exit 1
fi
print_ok "Docker found: $(docker --version | head -1)"

if ! docker info > /dev/null 2>&1; then
    print_error "Docker daemon is not running. Please start Docker."
    exit 1
fi
print_ok "Docker daemon is running"

if docker compose version > /dev/null 2>&1; then
    COMPOSE_CMD="docker compose"
elif command -v docker-compose &> /dev/null; then
    COMPOSE_CMD="docker-compose"
else
    print_error "Docker Compose is not installed."
    echo "       https://docs.docker.com/compose/install/"
    exit 1
fi
print_ok "Docker Compose found ($COMPOSE_CMD)"

# Auto-detect Docker API version
DOCKER_SERVER_API=$(docker version --format '{{.Server.APIVersion}}' 2>/dev/null || echo "")
if [ -n "$DOCKER_SERVER_API" ]; then
    export DOCKER_API_VERSION="$DOCKER_SERVER_API"
fi

# ═════════════════════════════════════════════════════════════════════════
# STEP 2: Interactive configuration
# ═════════════════════════════════════════════════════════════════════════

print_header "Step 2/5 - Configuration"

ENV_FILE=".env"
SKIP_CONFIG=false

if [ -f "$ENV_FILE" ]; then
    print_warn "An existing .env file was found."
    echo ""
    print_prompt "Do you want to reconfigure? (y/N): "
    read -r REPLY
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        print_info "Keeping existing configuration."
        SKIP_CONFIG=true
    fi
fi

if [ "$SKIP_CONFIG" = false ]; then

    echo -e "  ${DIM}Answer the following questions to configure Aegis."
    echo -e "  Press Enter to accept the default value shown in [brackets].${NC}"
    echo ""

    # ── Domain / URL ──────────────────────────────────────────────────

    echo -e "  ${BOLD}1. Domain Configuration${NC}"
    echo -e "  ${DIM}The domain where Aegis will be accessible."
    echo -e "  Examples: aegis.example.com, 192.168.1.100, localhost${NC}"
    echo ""
    print_prompt "Domain or IP [localhost]: "
    read -r INPUT_DOMAIN
    DOMAIN="${INPUT_DOMAIN:-localhost}"

    # ── Protocol ──────────────────────────────────────────────────────

    if [ "$DOMAIN" = "localhost" ] || [ "$DOMAIN" = "127.0.0.1" ]; then
        PROTOCOL="http"
        print_info "Using HTTP for local deployment"
    else
        echo ""
        print_prompt "Are you using HTTPS/SSL? (Y/n): "
        read -r REPLY
        if [[ $REPLY =~ ^[Nn]$ ]]; then
            PROTOCOL="http"
        else
            PROTOCOL="https"
        fi
    fi

    # ── Port ──────────────────────────────────────────────────────────

    if [ "$PROTOCOL" = "https" ]; then
        DEFAULT_PORT=443
    else
        DEFAULT_PORT=80
    fi

    echo ""
    echo -e "  ${BOLD}2. Port${NC}"
    print_prompt "Frontend port [$DEFAULT_PORT]: "
    read -r INPUT_PORT
    FRONTEND_PORT="${INPUT_PORT:-$DEFAULT_PORT}"

    # Build the full origin URL for CORS
    if { [ "$PROTOCOL" = "https" ] && [ "$FRONTEND_PORT" = "443" ]; } || \
       { [ "$PROTOCOL" = "http" ] && [ "$FRONTEND_PORT" = "80" ]; }; then
        ORIGIN_URL="${PROTOCOL}://${DOMAIN}"
    else
        ORIGIN_URL="${PROTOCOL}://${DOMAIN}:${FRONTEND_PORT}"
    fi

    # ── Admin account ─────────────────────────────────────────────────

    echo ""
    echo -e "  ${BOLD}3. Admin Account${NC}"
    echo -e "  ${DIM}The initial administrator account for Aegis.${NC}"
    echo ""
    print_prompt "Admin username [admin]: "
    read -r INPUT_ADMIN_USER
    ADMIN_USERNAME="${INPUT_ADMIN_USER:-admin}"

    echo ""
    echo -e "  ${DIM}Leave empty to auto-generate a secure password."
    echo -e "  The password will be shown in the installation summary.${NC}"
    print_prompt "Admin password [auto-generate]: "
    read -rs INPUT_ADMIN_PASS
    echo ""
    ADMIN_PASSWORD="${INPUT_ADMIN_PASS}"

    if [ -z "$ADMIN_PASSWORD" ]; then
        ADMIN_PASSWORD=$(gen_password 18)
        ADMIN_PW_GENERATED=true
        print_info "Password will be auto-generated"
    else
        ADMIN_PW_GENERATED=false
        print_ok "Password set"
    fi

    # ── Database ──────────────────────────────────────────────────────

    echo ""
    echo -e "  ${BOLD}4. Database${NC}"
    print_prompt "Database name [attackdb]: "
    read -r INPUT_DB_NAME
    DB_NAME="${INPUT_DB_NAME:-attackdb}"

    print_prompt "Database user [postgres]: "
    read -r INPUT_DB_USER
    DB_USER="${INPUT_DB_USER:-postgres}"

    echo -e "  ${DIM}Leave empty to auto-generate a secure password.${NC}"
    print_prompt "Database password [auto-generate]: "
    read -rs INPUT_DB_PASS
    echo ""
    if [ -z "$INPUT_DB_PASS" ]; then
        DB_PASSWORD=$(gen_password 24)
        print_info "Database password auto-generated"
    else
        DB_PASSWORD="$INPUT_DB_PASS"
        print_ok "Database password set"
    fi

    # ── Token expiry ──────────────────────────────────────────────────

    echo ""
    echo -e "  ${BOLD}5. Session Duration${NC}"
    print_prompt "Token expiry in minutes [60]: "
    read -r INPUT_TOKEN_EXP
    TOKEN_EXPIRE_MINUTES="${INPUT_TOKEN_EXP:-60}"

    # ── MITRE sync ────────────────────────────────────────────────────

    echo ""
    echo -e "  ${BOLD}6. Initial Data${NC}"
    print_prompt "Run MITRE ATT&CK sync after install? (Y/n): "
    read -r INPUT_SYNC
    if [[ $INPUT_SYNC =~ ^[Nn]$ ]]; then
        RUN_MITRE_SYNC=false
    else
        RUN_MITRE_SYNC=true
    fi

    # ── Generate secrets ──────────────────────────────────────────────

    SECRET_KEY=$(gen_secret 32)
    MINIO_ACCESS=$(gen_secret 8)
    MINIO_SECRET=$(gen_password 24)

    # ── Show summary before writing ──────────────────────────────────

    echo ""
    echo -e "${BOLD}    ┌──────────────────────────────────────────────────────┐${NC}"
    echo -e "${BOLD}    │            Configuration Summary                     │${NC}"
    echo -e "${BOLD}    ├──────────────────────────────────────────────────────┤${NC}"
    echo -e "    │  URL:           ${CYAN}${ORIGIN_URL}${NC}"
    echo -e "    │  Admin user:    ${CYAN}${ADMIN_USERNAME}${NC}"
    if [ "$ADMIN_PW_GENERATED" = true ]; then
    echo -e "    │  Admin pass:    ${CYAN}(auto-generated)${NC}"
    else
    echo -e "    │  Admin pass:    ${CYAN}(custom)${NC}"
    fi
    echo -e "    │  Database:      ${CYAN}${DB_USER}@${DB_NAME}${NC}"
    echo -e "    │  Port:          ${CYAN}${FRONTEND_PORT}${NC}"
    echo -e "    │  Session TTL:   ${CYAN}${TOKEN_EXPIRE_MINUTES} min${NC}"
    echo -e "    │  MITRE sync:    ${CYAN}$([ "$RUN_MITRE_SYNC" = true ] && echo "yes" || echo "no")${NC}"
    echo -e "${BOLD}    └──────────────────────────────────────────────────────┘${NC}"
    echo ""
    # Warn about data loss if containers/volumes already exist
    if $COMPOSE_CMD -f docker-compose.prod.yml ps -q 2>/dev/null | grep -q . || \
       docker volume ls --format '{{.Name}}' 2>/dev/null | grep -qi 'postgres'; then
        echo ""
        print_warn "Existing database volumes detected."
        print_warn "Proceeding will RESET the database — all existing data will be lost."
    fi

    print_prompt "Proceed with these settings? (Y/n): "
    read -r CONFIRM
    if [[ $CONFIRM =~ ^[Nn]$ ]]; then
        print_warn "Installation cancelled. Run the script again to reconfigure."
        exit 0
    fi

    # ── Write .env ────────────────────────────────────────────────────

    cat > "$ENV_FILE" <<ENVEOF
# =============================================================================
# Aegis Production Environment
# Generated by install.sh on $(date -u +"%Y-%m-%d %H:%M:%S UTC")
# =============================================================================

# ── Database ─────────────────────────────────────────────────────────────────
DB_USER=${DB_USER}
DB_PASSWORD=${DB_PASSWORD}
DB_NAME=${DB_NAME}

# ── Security ─────────────────────────────────────────────────────────────────
SECRET_KEY=${SECRET_KEY}
TOKEN_EXPIRE_MINUTES=${TOKEN_EXPIRE_MINUTES}

# ── Initial Admin Account ────────────────────────────────────────────────────
ADMIN_USERNAME=${ADMIN_USERNAME}
ADMIN_PASSWORD=${ADMIN_PASSWORD}

# ── MinIO Object Storage ─────────────────────────────────────────────────────
MINIO_ACCESS_KEY=${MINIO_ACCESS}
MINIO_SECRET_KEY=${MINIO_SECRET}
MINIO_BUCKET=evidence

# ── CORS (allowed frontend origins) ─────────────────────────────────────────
CORS_ORIGINS=${ORIGIN_URL}

# ── Frontend ─────────────────────────────────────────────────────────────────
FRONTEND_PORT=${FRONTEND_PORT}

# ── Environment ──────────────────────────────────────────────────────────────
AEGIS_ENV=production
ENVEOF

    print_ok ".env file created with secure configuration"

fi  # end SKIP_CONFIG

# ═════════════════════════════════════════════════════════════════════════
# STEP 3: Build and start
# ═════════════════════════════════════════════════════════════════════════

print_header "Step 3/5 - Building and starting containers"

print_info "This may take several minutes on first run..."

# When reconfiguring, remove old volumes so Postgres re-initializes with the new password.
# Without this, Postgres ignores the new DB_PASSWORD because it only sets credentials
# on first initialization (empty volume) — leaving the backend unable to authenticate.
if [ "$SKIP_CONFIG" = false ]; then
    print_info "Removing existing volumes to apply new credentials..."
    $COMPOSE_CMD -f docker-compose.prod.yml down -v > /dev/null 2>&1 || true
fi

if ! $COMPOSE_CMD -f docker-compose.prod.yml up -d --build 2>&1; then
    print_error "Failed to build/start containers. Check the output above."
    exit 1
fi

print_ok "Containers started"

# ═════════════════════════════════════════════════════════════════════════
# STEP 4: Wait for services
# ═════════════════════════════════════════════════════════════════════════

print_header "Step 4/5 - Waiting for services to be ready"

# Wait for PostgreSQL
echo -en "  PostgreSQL ..."
MAX_RETRIES=30
RETRY=0
until docker exec aegis-postgres pg_isready -U postgres > /dev/null 2>&1; do
    RETRY=$((RETRY + 1))
    if [ $RETRY -ge $MAX_RETRIES ]; then
        echo ""
        print_error "PostgreSQL failed to start. Check: docker logs aegis-postgres"
        exit 1
    fi
    echo -n "."
    sleep 2
done
echo -e " ${GREEN}ready${NC}"

# Wait for backend (runs migrations + seed)
echo -en "  Backend (migrations + seed) ..."
RETRY=0
until docker exec aegis-backend curl -sf http://localhost:8000/health > /dev/null 2>&1; do
    RETRY=$((RETRY + 1))
    if [ $RETRY -ge 90 ]; then
        echo ""
        print_error "Backend failed to start after 3 minutes."
        echo "         Check: docker logs aegis-backend"
        exit 1
    fi
    echo -n "."
    sleep 2
done
echo -e " ${GREEN}ready${NC}"

# Wait for frontend
FRONTEND_PORT=$(grep FRONTEND_PORT "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "80")
FRONTEND_PORT=${FRONTEND_PORT:-80}

echo -en "  Frontend ..."
RETRY=0
until curl -sf "http://localhost:${FRONTEND_PORT}" > /dev/null 2>&1; do
    RETRY=$((RETRY + 1))
    if [ $RETRY -ge 30 ]; then
        echo ""
        print_error "Frontend failed to start. Check: docker logs aegis-frontend"
        exit 1
    fi
    echo -n "."
    sleep 2
done
echo -e " ${GREEN}ready${NC}"

print_ok "All services are running"

# ── Extract admin credentials from backend logs ──────────────────────

ADMIN_CREDS_USER=""
ADMIN_CREDS_PASS=""

# Try to extract the credentials from the backend startup logs
LOG_OUTPUT=$(docker logs aegis-backend 2>&1 | tail -20)

if echo "$LOG_OUTPUT" | grep -q "Initial Admin User Created"; then
    ADMIN_CREDS_USER=$(echo "$LOG_OUTPUT" | grep "Username :" | sed 's/.*Username : //')
    ADMIN_CREDS_PASS=$(echo "$LOG_OUTPUT" | grep "Password :" | sed 's/.*Password : //')
fi

# Fallback: if we set it via env, use those values
if [ -z "$ADMIN_CREDS_USER" ]; then
    ADMIN_CREDS_USER=$(grep ADMIN_USERNAME "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "admin")
    ADMIN_CREDS_USER="${ADMIN_CREDS_USER:-admin}"
fi

if [ -z "$ADMIN_CREDS_PASS" ] || [ "$ADMIN_CREDS_PASS" = "(set via ADMIN_PASSWORD env var)" ]; then
    ADMIN_CREDS_PASS=$(grep ADMIN_PASSWORD "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "")
fi

# ═════════════════════════════════════════════════════════════════════════
# STEP 5: Initial data sync (optional)
# ═════════════════════════════════════════════════════════════════════════

# Re-read RUN_MITRE_SYNC if we skipped config
if [ "$SKIP_CONFIG" = true ]; then
    echo ""
    print_prompt "Run initial MITRE ATT&CK sync? (~700 techniques, 1-2 min) (Y/n): "
    read -r INPUT_SYNC
    if [[ $INPUT_SYNC =~ ^[Nn]$ ]]; then
        RUN_MITRE_SYNC=false
    else
        RUN_MITRE_SYNC=true
    fi
fi

if [ "$RUN_MITRE_SYNC" = true ]; then
    print_header "Step 5/5 - Initial data sync"

    print_info "Authenticating with backend..."

    # Authenticate via backend container (most reliable)
    TOKEN=$(docker exec aegis-backend curl -sf -X POST "http://localhost:8000/api/v1/auth/login" \
        -H "Content-Type: application/x-www-form-urlencoded" \
        -d "username=${ADMIN_CREDS_USER}&password=${ADMIN_CREDS_PASS}" 2>/dev/null | \
        python3 -c "import sys,json; print(json.load(sys.stdin).get('access_token',''))" 2>/dev/null || echo "")

    if [ -n "$TOKEN" ] && [ "$TOKEN" != "" ]; then

        # MITRE ATT&CK sync
        print_info "Syncing MITRE ATT&CK techniques (1-2 minutes)..."
        SYNC_RESULT=$(docker exec aegis-backend curl -sf --max-time 300 \
            -X POST "http://localhost:8000/api/v1/system/sync-mitre" \
            -H "Authorization: Bearer $TOKEN" 2>/dev/null || echo "error")

        if [ "$SYNC_RESULT" != "error" ]; then
            NEW_TECHNIQUES=$(echo "$SYNC_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('new',0))" 2>/dev/null || echo "?")
            print_ok "MITRE sync completed ($NEW_TECHNIQUES techniques imported)"
        else
            print_warn "MITRE sync timed out. You can retry from the System page."
        fi

        # Data sources sync
        print_info "Syncing data sources (Atomic Red Team, SigmaHQ, etc.)..."
        SOURCES=$(docker exec aegis-backend curl -sf "http://localhost:8000/api/v1/data-sources" \
            -H "Authorization: Bearer $TOKEN" 2>/dev/null | \
            python3 -c "import sys,json; [print(s['id']) for s in json.load(sys.stdin)]" 2>/dev/null || echo "")

        SYNC_COUNT=0
        for source_id in $SOURCES; do
            docker exec aegis-backend curl -sf --max-time 120 \
                -X POST "http://localhost:8000/api/v1/data-sources/${source_id}/sync" \
                -H "Authorization: Bearer $TOKEN" > /dev/null 2>&1 && SYNC_COUNT=$((SYNC_COUNT + 1)) || true
        done
        if [ "$SYNC_COUNT" -gt 0 ]; then
            print_ok "Data sources synced ($SYNC_COUNT sources)"
        fi
    else
        print_warn "Could not authenticate. Run MITRE sync from the System page."
    fi
else
    print_header "Step 5/5 - Skipping data sync"
    print_info "You can import data later from the System page."
fi

# ═════════════════════════════════════════════════════════════════════════
# FINAL SUMMARY
# ═════════════════════════════════════════════════════════════════════════

# Build the access URL
ORIGIN_URL=$(grep CORS_ORIGINS "$ENV_FILE" 2>/dev/null | cut -d= -f2 || echo "http://localhost")
SERVER_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "localhost")

echo ""
echo ""
echo -e "${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BOLD}║                                                              ║${NC}"
echo -e "${BOLD}║            ${GREEN}Aegis is ready!${NC}${BOLD}                                  ║${NC}"
echo -e "${BOLD}║                                                              ║${NC}"
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  ${CYAN}Application${NC}  ${ORIGIN_URL}"
echo -e "${BOLD}║${NC}  ${CYAN}Local IP${NC}     http://${SERVER_IP}:${FRONTEND_PORT}"
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  ${CYAN}Admin Login${NC}                                                 ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  Username:   ${GREEN}${ADMIN_CREDS_USER}${NC}"
if [ -n "$ADMIN_CREDS_PASS" ]; then
echo -e "${BOLD}║${NC}  Password:   ${GREEN}${ADMIN_CREDS_PASS}${NC}"
else
echo -e "${BOLD}║${NC}  Password:   ${YELLOW}(check: docker logs aegis-backend | grep Password)${NC}"
fi
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}╠══════════════════════════════════════════════════════════════╣${NC}"
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  ${YELLOW}Important:${NC}                                                  ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  - Save the admin password now if auto-generated             ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  - Set up HTTPS/TLS for internet-facing deployments          ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  - Configure firewall rules as needed                        ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}  - Set up regular database backups                           ${BOLD}║${NC}"
echo -e "${BOLD}║${NC}                                                              ${BOLD}║${NC}"
echo -e "${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "${BOLD}Useful commands:${NC}"
echo -e "  ${DIM}View logs${NC}       docker logs -f aegis-backend"
echo -e "  ${DIM}Stop${NC}            $COMPOSE_CMD -f docker-compose.prod.yml down"
echo -e "  ${DIM}Restart${NC}         $COMPOSE_CMD -f docker-compose.prod.yml restart"
echo -e "  ${DIM}Update${NC}          $COMPOSE_CMD -f docker-compose.prod.yml up -d --build"
echo -e "  ${DIM}DB backup${NC}       docker exec aegis-postgres pg_dump -U postgres ${DB_NAME:-attackdb} > backup.sql"
echo -e "  ${DIM}Reconfigure${NC}     ./scripts/install.sh"
echo ""
