#!/bin/bash

# Container Manager Script for WHP
# This script manages system Docker containers (MySQL, HAProxy Manager, Memcache)
# with support for API key configuration and container lifecycle operations

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Function to print colored output
print_status() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

print_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

print_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

# Function to get HAProxy API key from settings
get_haproxy_api_key() {
    local api_key=""
    if [ -f "/docker/whp/settings.json" ]; then
        # Try jq first (more reliable for JSON parsing)
        if command -v jq >/dev/null 2>&1; then
            api_key=$(jq -r '.haproxy_api_key // empty' /docker/whp/settings.json 2>/dev/null || echo "")
        else
            # Fallback to grep/sed method
            api_key=$(sed -n 's/.*"haproxy_api_key":"\([^"]*\)".*/\1/p' /docker/whp/settings.json 2>/dev/null || echo "")
        fi
    fi
    echo "$api_key"
}

# Function to get MySQL password
get_mysql_password() {
    local mysql_password=""
    if [ -f "/root/.my.cnf" ]; then
        mysql_password=$(grep '^password=' /root/.my.cnf | cut -d'=' -f2-)
    fi
    echo "$mysql_password"
}

# Function to get PostgreSQL password
get_postgresql_password() {
    local postgres_password=""
    if [ -f "/root/.pgpass" ]; then
        postgres_password=$(cut -d: -f5 /root/.pgpass | head -1)
    fi
    echo "$postgres_password"
}

# Function to recreate HAProxy container
recreate_haproxy() {
    print_status "Recreating HAProxy container..."
    
    # Stop and remove existing container
    if docker ps -a --filter name=haproxy-manager --format '{{.Names}}' | grep -q haproxy-manager; then
        print_status "Stopping existing HAProxy container..."
        docker stop haproxy-manager 2>/dev/null || true
        print_status "Removing existing HAProxy container..."
        docker rm haproxy-manager 2>/dev/null || true
    fi
    
    # Pull latest image
    print_status "Pulling latest HAProxy image..."
    docker pull repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest
    
    # Get API key
    local api_key=$(get_haproxy_api_key)
    local api_key_env=""

    if [ -n "$api_key" ]; then
        api_key_env="-e HAPROXY_API_KEY=$api_key "
        print_status "Using API key from settings"
    else
        print_warning "No API key found in settings - container will start without API key"
    fi

    # Coraza WAF integration: when settings.json says coraza_waf.mode is
    # detect_only or enforce, pass HAPROXY_CORAZA_SPOE_BACKEND. The mirrored
    # logic in haproxy-manager's haproxy_manager.py renders the SPOE filter
    # blocks into haproxy.cfg when this is set. Default mode is off (and
    # missing key) -> no env var, no SPOE rendering, no WAF.
    local coraza_env=""
    if [ -f "/docker/whp/settings.json" ] && command -v jq >/dev/null 2>&1; then
        local waf_mode=$(jq -r '.coraza_waf.mode // "off"' /docker/whp/settings.json 2>/dev/null || echo "off")
        if [ "$waf_mode" = "detect_only" ] || [ "$waf_mode" = "enforce" ]; then
            coraza_env="-e HAPROXY_CORAZA_SPOE_BACKEND=coraza-spoa:9000 "
            print_status "WAF mode: $waf_mode — enabling SPOE integration"
        fi
    fi

    # Site-suspension routing: when HAPROXY_SUSPENSION_ENABLED=true, the
    # haproxy-manager image renders an ACL that rewrites the path to
    # /suspended and routes through default-backend (the Flask app inside
    # the haproxy-manager container itself, on port 8080) for any host
    # listed in /etc/haproxy/suspended_domains.list. site_disable.php
    # maintains that list via `docker cp ... haproxy-manager:/etc/haproxy/`.
    # Always-on for WHP.
    local suspension_env="-e HAPROXY_SUSPENSION_ENABLED=true "

    # Start new container
    print_status "Starting new HAProxy container..."
    docker run -d --network=client-net --add-host=host.docker.internal:host-gateway \
        -p 80:80 -p 443:443 -p 8000:8000 \
        -v lets-encrypt:/etc/letsencrypt -v haproxy:/etc/haproxy \
        --memory=1g --memory-swap=2g \
        --health-cmd="curl -s -o /dev/null --max-time 10 http://localhost/ || exit 1" \
        --health-interval=30s \
        --health-start-period=60s \
        --health-timeout=10s \
        --health-retries=3 \
        $api_key_env$coraza_env$suspension_env--name haproxy-manager --restart unless-stopped \
        --label whp.tier=edge \
        repo.anhonesthost.net/cloud-hosting-platform/haproxy-manager-base:latest
    
    # Verify container is running
    if docker ps --filter name=haproxy-manager --format '{{.Status}}' | grep -q "Up"; then
        print_success "HAProxy container recreated and running successfully"
    else
        print_error "Failed to start HAProxy container"
        exit 1
    fi
}

# Function to recreate the Coraza-SPOA sidecar.
# Delegates the actual lifecycle to apply-coraza-waf-mode.sh, which reads
# settings.json's coraza_waf.mode and decides whether to run the sidecar
# (and how — with the empty-overrides bind mount in detect_only, or with
# baked-in enforce-list overrides in enforce mode).
recreate_coraza_spoa() {
    print_status "Recreating Coraza-SPOA sidecar..."
    local apply_script="/root/whp/scripts/apply-coraza-waf-mode.sh"
    if [ ! -x "$apply_script" ]; then
        print_error "$apply_script not found or not executable"
        exit 1
    fi
    "$apply_script"
}

# Function to recreate MySQL container
recreate_mysql() {
    print_status "Recreating MySQL container..."
    
    # Stop and remove existing container
    if docker ps -a --filter name=mysql --format '{{.Names}}' | grep -q mysql; then
        print_status "Stopping existing MySQL container..."
        docker stop mysql 2>/dev/null || true
        print_status "Removing existing MySQL container..."
        docker rm mysql 2>/dev/null || true
    fi
    
    # Pull latest image
    print_status "Pulling latest MySQL image..."
    docker pull mariadb:10.11
    
    # Get MySQL password
    local mysql_password=$(get_mysql_password)
    if [ -z "$mysql_password" ]; then
        print_error "Could not determine MySQL password from /root/.my.cnf"
        exit 1
    fi
    
    # Start new container (bind to localhost only for security)
    print_status "Starting new MySQL container..."
    docker run -d --network=client-net --name mysql \
        --env MARIADB_ROOT_PASSWORD="$mysql_password" \
        -v /docker/mysql:/var/lib/mysql:Z -p 127.0.0.1:3306:3306 \
        --restart unless-stopped --label whp.tier=db mariadb:10.11
    
    # Verify container is running
    if docker ps --filter name=mysql --format '{{.Status}}' | grep -q "Up"; then
        print_success "MySQL container recreated and running successfully"
    else
        print_error "Failed to start MySQL container"
        exit 1
    fi
}

# Function to recreate Memcache container
recreate_memcache() {
    print_status "Recreating Memcache container..."
    
    # Stop and remove existing container
    if docker ps -a --filter name=memcache --format '{{.Names}}' | grep -q memcache; then
        print_status "Stopping existing Memcache container..."
        docker stop memcache 2>/dev/null || true
        print_status "Removing existing Memcache container..."
        docker rm memcache 2>/dev/null || true
    fi
    
    # Pull latest image
    print_status "Pulling latest Memcache image..."
    docker pull repo.anhonesthost.net/cloud-hosting-platform/cmc:latest
    
    # Start new container
    print_status "Starting new Memcache container..."
    docker run -d --network=client-net --add-host=host.docker.internal:host-gateway \
        -p 11211:11211 --restart unless-stopped --name memcache \
        --label whp.tier=edge \
        repo.anhonesthost.net/cloud-hosting-platform/cmc:latest
    
    # Verify container is running
    if docker ps --filter name=memcache --format '{{.Status}}' | grep -q "Up"; then
        print_success "Memcache container recreated and running successfully"
    else
        print_error "Failed to start Memcache container"
        exit 1
    fi
}

# Function to recreate shared-httpd container(s)
recreate_shared_httpd() {
    local instance=${1:-01}
    local name="shared-httpd-${instance}"

    print_status "Recreating shared httpd container: $name..."

    # Stop and remove existing container
    if docker ps -a --filter name="^${name}$" --format '{{.Names}}' | grep -q "$name"; then
        print_status "Stopping existing $name container..."
        docker stop "$name" 2>/dev/null || true
        print_status "Removing existing $name container..."
        docker rm "$name" 2>/dev/null || true
    fi

    # Pull latest image
    print_status "Pulling latest shared-httpd image..."
    docker pull repo.anhonesthost.net/cloud-hosting-platform/shared-httpd:latest

    # Create vhost config directory if needed
    mkdir -p /docker/shared-httpd/vhosts

    # Start new container
    print_status "Starting new $name container..."
    docker run -d --network=client-net --add-host=host.docker.internal:host-gateway \
        -v /docker/shared-httpd/vhosts:/etc/httpd/conf.d/vhosts:ro \
        -v /docker/users:/mnt/users:ro \
        --memory=1g --memory-swap=2g \
        --health-cmd="curl -sfk https://localhost/ping || exit 1" \
        --health-interval=30s \
        --health-start-period=60s \
        --health-timeout=10s \
        --health-retries=3 \
        --name "$name" --restart unless-stopped \
        --label whp.tier=edge \
        repo.anhonesthost.net/cloud-hosting-platform/shared-httpd:latest

    # Verify container is running
    if docker ps --filter name="^${name}$" --format '{{.Status}}' | grep -q "Up"; then
        print_success "$name container recreated and running successfully"
    else
        print_error "Failed to start $name container"
        exit 1
    fi
}

# Function to scale shared-httpd instances
scale_shared_httpd() {
    local target=${1:-1}

    print_status "Scaling shared-httpd to $target instance(s)..."

    # Get current running instances
    local current_instances=()
    while IFS= read -r line; do
        [ -n "$line" ] && current_instances+=("$line")
    done < <(docker ps --filter 'name=shared-httpd-' --filter 'status=running' --format '{{.Names}}' 2>/dev/null | sort)

    local current_count=${#current_instances[@]}
    print_status "Currently running: $current_count instance(s)"

    # Start new instances if needed
    if [ "$target" -gt "$current_count" ]; then
        for i in $(seq $((current_count + 1)) $target); do
            local instance=$(printf "%02d" $i)
            recreate_shared_httpd "$instance"
        done
    fi

    # Remove excess instances if needed
    local scaled_down=false
    if [ "$target" -lt "$current_count" ]; then
        for i in $(seq $((target + 1)) $current_count); do
            local instance=$(printf "%02d" $i)
            local name="shared-httpd-${instance}"
            print_status "Removing excess instance: $name"
            docker stop "$name" 2>/dev/null || true
            docker rm "$name" 2>/dev/null || true
            print_success "Removed $name"
        done
        scaled_down=true
    fi

    # After scaling, reconfigure HAProxy for all shared_httpd sites
    # so backends point to the current set of instances
    print_status "Updating HAProxy backends for shared_httpd sites..."
    reconfigure_shared_httpd_haproxy

    print_success "Shared-httpd scaled to $target instance(s)"
}

# Function to reconfigure HAProxy backends for all shared_httpd sites
# Called after scaling shared-httpd instances up or down
reconfigure_shared_httpd_haproxy() {
    # Use PHP to reconfigure all shared_httpd sites via the haproxy_manager
    php -r '
        require_once("/docker/whp/web/libs/mysqlmgmt.php");
        require_once("/docker/whp/web/libs/haproxy_manager.php");

        $MySQLMgmt = new mysqlmgmt();
        $db = $MySQLMgmt->getMySQLConnection();
        if (!$db) { echo "ERROR: Cannot connect to database\n"; exit(1); }

        // Find all shared_httpd sites
        $stmt = $db->query("SELECT id, site_name FROM whp.sites WHERE container_mode = \"shared_httpd\"");
        $sites = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (empty($sites)) {
            echo "No shared_httpd sites to reconfigure\n";
            exit(0);
        }

        $haproxy = new haproxy_manager();
        $success = 0;
        $failed = 0;

        foreach ($sites as $site) {
            $result = $haproxy->configureSite($site["id"], []);
            if ($result["success"]) {
                $success++;
            } else {
                echo "WARN: Failed to configure site " . $site["site_name"] . ": " . ($result["error"] ?? "unknown") . "\n";
                $failed++;
            }
        }

        echo "Reconfigured $success shared_httpd site(s)";
        if ($failed > 0) echo ", $failed failed";
        echo "\n";
    ' 2>&1

    if [ $? -eq 0 ]; then
        print_success "HAProxy backends updated for shared_httpd sites"
    else
        print_error "Failed to update HAProxy backends"
    fi
}

# Function to recreate PostgreSQL container
recreate_postgresql() {
    print_status "Recreating PostgreSQL container..."
    
    # Stop and remove existing container
    if docker ps -a --filter name=postgresql --format '{{.Names}}' | grep -q postgresql; then
        print_status "Stopping existing PostgreSQL container..."
        docker stop postgresql 2>/dev/null || true
        print_status "Removing existing PostgreSQL container..."
        docker rm postgresql 2>/dev/null || true
    fi
    
    # Pull latest image
    print_status "Pulling latest PostgreSQL image..."
    docker pull postgres:latest
    
    # Get PostgreSQL password
    local postgres_password=$(get_postgresql_password)
    if [ -z "$postgres_password" ]; then
        print_warning "PostgreSQL password not found in /root/.pgpass - skipping PostgreSQL recreation"
        print_warning "If PostgreSQL is installed, please check /root/.pgpass configuration"
        return 0
    fi
    
    # Start new container (bind to localhost only for security)
    print_status "Starting new PostgreSQL container..."
    docker run -d --network=client-net --name postgresql \
        --env POSTGRES_PASSWORD="$postgres_password" \
        -v /docker/postgresql:/var/lib/postgresql/data:Z -p 127.0.0.1:5432:5432 \
        --restart unless-stopped --label whp.tier=db postgres:latest
    
    # Verify container is running
    if docker ps --filter name=postgresql --format '{{.Status}}' | grep -q "Up"; then
        print_success "PostgreSQL container recreated and running successfully"
    else
        print_error "Failed to start PostgreSQL container"
        exit 1
    fi
}

# Function to show container status
show_status() {
    print_status "Container Status:"
    echo ""
    
    local containers=("mysql" "postgresql" "haproxy-manager" "memcache")

    for container in "${containers[@]}"; do
        local status=$(docker ps --filter name="^${container}$" --format '{{.Status}}' 2>/dev/null)
        if [ -n "$status" ]; then
            local health=$(docker inspect --format='{{if .State.Health}}{{.State.Health.Status}}{{end}}' $container 2>/dev/null)
            if [ -n "$health" ]; then
                print_success "$container: Running ($status) [Health: $health]"
            else
                print_success "$container: Running ($status)"
            fi
        else
            local exists=$(docker ps -a --filter name="^${container}$" --format '{{.Names}}' 2>/dev/null)
            if [ -n "$exists" ]; then
                print_warning "$container: Stopped"
            else
                print_error "$container: Not found"
            fi
        fi
    done

    # Show shared-httpd instances separately
    echo ""
    print_status "Shared httpd Instances:"
    local shared_found=false
    while IFS= read -r name; do
        [ -z "$name" ] && continue
        shared_found=true
        local status=$(docker ps --filter name="^${name}$" --format '{{.Status}}' 2>/dev/null)
        local health=$(docker inspect --format='{{if .State.Health}}{{.State.Health.Status}}{{end}}' "$name" 2>/dev/null)
        if [ -n "$status" ]; then
            if [ -n "$health" ]; then
                print_success "$name: Running ($status) [Health: $health]"
            else
                print_success "$name: Running ($status)"
            fi
        else
            print_warning "$name: Stopped"
        fi
    done < <(docker ps -a --filter 'name=shared-httpd-' --format '{{.Names}}' 2>/dev/null | sort)
    if [ "$shared_found" = false ]; then
        print_warning "No shared-httpd instances found"
    fi
}

# NOTE: the whp-suspended container was removed. Suspension page is now
# served by the haproxy-manager container's built-in Flask /suspended
# route (same Flask app that serves /default-page and /blocked-ip).
# This function remains as a no-op that cleans up any leftover instance,
# so `recreate whp-suspended` on a host that previously deployed the old
# design doesn't fail.
recreate_whp_suspended() {
    if docker ps -a --filter name=whp-suspended --format '{{.Names}}' | grep -q whp-suspended; then
        print_status "Removing legacy whp-suspended container (page is now served by haproxy-manager)"
        docker stop whp-suspended 2>/dev/null || true
        docker rm whp-suspended 2>/dev/null || true
        print_success "Legacy whp-suspended container removed"
    else
        print_status "No legacy whp-suspended container present; nothing to do"
    fi
}

# Function to show help
show_help() {
    echo "Container Manager Script for WHP"
    echo ""
    echo "Usage: $0 [COMMAND] [CONTAINER]"
    echo ""
    echo "Commands:"
    echo "  status                    Show status of all containers"
    echo "  start <container>         Start a specific container"
    echo "  stop <container>          Stop a specific container"
    echo "  restart <container>       Restart a specific container"
    echo "  recreate <container>      Pull latest image and recreate container"
    echo "  recreate-all              Recreate all containers"
    echo "  scale shared-httpd <N>    Scale shared-httpd to N instances"
    echo "  help                      Show this help message"
    echo ""
    echo "Containers:"
    echo "  mysql                     MariaDB database container"
    echo "  postgresql                PostgreSQL database container"
    echo "  haproxy-manager           HAProxy Manager container (with API key support)"
    echo "  coraza-spoa               Coraza WAF SPOA sidecar (mode driven by settings.json: coraza_waf.mode)"
    echo "  memcache                  Memcache container"
    echo "  shared-httpd [NN]         Shared Apache httpd instance (default: 01)"
    echo "  whp-suspended             (legacy) Removes any leftover whp-suspended container; the page is served by haproxy-manager now"
    echo ""
    echo "Examples:"
    echo "  $0 status"
    echo "  $0 start mysql"
    echo "  $0 recreate haproxy-manager"
    echo "  $0 recreate shared-httpd          # Recreate instance 01"
    echo "  $0 recreate shared-httpd 02       # Recreate instance 02"
    echo "  $0 scale shared-httpd 3           # Scale to 3 instances"
    echo "  $0 recreate-all"
}

# Main script logic
case "${1:-help}" in
    "status")
        show_status
        ;;
    "start")
        if [ -z "$2" ]; then
            print_error "Container name required"
            exit 1
        fi
        print_status "Starting container: $2"
        docker start "$2"
        print_success "Container $2 started"
        ;;
    "stop")
        if [ -z "$2" ]; then
            print_error "Container name required"
            exit 1
        fi
        print_status "Stopping container: $2"
        docker stop "$2"
        print_success "Container $2 stopped"
        ;;
    "restart")
        if [ -z "$2" ]; then
            print_error "Container name required"
            exit 1
        fi
        print_status "Restarting container: $2"
        docker restart "$2"
        print_success "Container $2 restarted"
        ;;
    "recreate")
        if [ -z "$2" ]; then
            print_error "Container name required"
            exit 1
        fi
        case "$2" in
            "mysql")
                recreate_mysql
                ;;
            "haproxy-manager")
                recreate_haproxy
                ;;
            "memcache")
                recreate_memcache
                ;;
            "postgresql")
                recreate_postgresql
                ;;
            "shared-httpd")
                recreate_shared_httpd "${3:-01}"
                ;;
            "coraza-spoa")
                recreate_coraza_spoa
                ;;
            "whp-suspended")
                recreate_whp_suspended
                ;;
            *)
                print_error "Unknown container: $2"
                print_error "Valid containers: mysql, postgresql, haproxy-manager, memcache, shared-httpd, coraza-spoa, whp-suspended"
                exit 1
                ;;
        esac
        ;;
    "scale")
        if [ "$2" != "shared-httpd" ]; then
            print_error "Scale is only supported for shared-httpd"
            exit 1
        fi
        if [ -z "$3" ] || ! [[ "$3" =~ ^[0-9]+$ ]]; then
            print_error "Usage: $0 scale shared-httpd <number>"
            exit 1
        fi
        scale_shared_httpd "$3"
        ;;
    "recreate-all")
        print_status "Recreating all containers..."
        recreate_mysql
        # Only recreate PostgreSQL if it's configured
        if [ -f "/root/.pgpass" ] && [ -n "$(get_postgresql_password)" ]; then
            recreate_postgresql
        fi
        recreate_haproxy
        recreate_memcache
        # Recreate shared-httpd instances if any exist
        if docker ps -a --filter 'name=shared-httpd-' --format '{{.Names}}' 2>/dev/null | grep -q .; then
            print_status "Recreating shared-httpd instances..."
            while IFS= read -r name; do
                [ -z "$name" ] && continue
                local instance="${name#shared-httpd-}"
                recreate_shared_httpd "$instance"
            done < <(docker ps -a --filter 'name=shared-httpd-' --format '{{.Names}}' 2>/dev/null | sort)
        fi
        print_success "All containers recreated successfully"
        ;;
    "help"|*)
        show_help
        ;;
esac 