#!/bin/bash

# docker-deployment-manager.sh - Manage Docker stack deployments across multiple servers
# Author: Shell Repository
# Description: Deploy specific Docker stacks to designated servers while maintaining monorepo structure

set -e

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

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DOCKER_DIR="$HOME/docker"
DEPLOYMENT_CONFIG_DIR="$HOME/.docker-deployment"
LOG_FILE="$SCRIPT_DIR/logs/deployment.log"

# Ensure directories exist
mkdir -p "$(dirname "$LOG_FILE")"
mkdir -p "$DEPLOYMENT_CONFIG_DIR"/{config,servers,stacks,logs}

# Logging function
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

# Display usage information
usage() {
    echo "Usage: $0 [OPTIONS] [COMMAND]"
    echo ""
    echo "Manage Docker stack deployments across multiple servers"
    echo ""
    echo "Commands:"
    echo "  init                    Initialize deployment configuration"
    echo "  map                     Show stack-to-server mapping"
    echo "  deploy SERVER           Deploy stacks to specific server"
    echo "  deploy-all              Deploy all stacks to their designated servers"
    echo "  status SERVER           Check deployment status on server"
    echo "  sync-env SERVER         Sync environment files to server"
    echo "  rollback SERVER         Rollback to previous deployment"
    echo ""
    echo "Options:"
    echo "  -h, --help              Show this help message"
    echo "  -d, --dry-run           Show what would be deployed without doing it"
    echo "  -f, --force             Force deployment even if checks fail"
    echo "  -v, --verbose           Verbose output"
    echo "  --config-only           Only sync configuration files"
    echo "  --env-only              Only sync environment files"
    echo ""
    echo "Examples:"
    echo "  $0 init                     # First time setup"
    echo "  $0 map                      # See what goes where"
    echo "  $0 deploy europa            # Deploy Europa stacks"
    echo "  $0 deploy-all --dry-run     # Test full deployment"
    echo "  $0 sync-env io              # Sync .env files to IO server"
}

# Initialize deployment configuration
init_deployment_config() {
    echo -e "${YELLOW}Initializing Docker deployment configuration...${NC}"

    # Create main configuration file
    cat > "$DEPLOYMENT_CONFIG_DIR/config.yml" << 'EOF'
# Docker Deployment Manager Configuration
# This file defines global settings for stack deployment across servers

deployment:
  version: "1.0"
  docker_dir: "~/docker"
  backup_before_deploy: true
  health_check_timeout: 30
  rollback_on_failure: true

# Multi-server stacks - these will be deployed to ALL servers
multi_server_stacks:
  - dozzle      # Docker log viewer
  - dockge      # Docker compose management
  - diun        # Docker image update notifier

notifications:
  enabled: true
  webhook_url: "https://notify.peterwood.rocks/lab"
  tags: ["deployment", "docker"]

logging:
  level: "info"
  retain_days: 30

security:
  verify_checksums: true
  backup_env_files: true
  use_secure_transfer: true
EOF

    # Create server configurations based on existing crontab analysis
    cat > "$DEPLOYMENT_CONFIG_DIR/servers/europa.yml" << 'EOF'
# Europa Server Configuration - Media Server
name: "europa"
role: "media-server"
description: "Primary media streaming and web services server"

connection:
  hostname: "europa"
  user: "acedanger"
  ssh_key: "~/.ssh/id_rsa"
  docker_compose_dir: "~/docker"

stacks:
  - plex
  - jellyfin
  - traefik
  - nextcloud
  - photoprism
  - immich

resources:
  cpu_cores: 4
  memory_gb: 8
  storage_gb: 500

monitoring:
  health_check_url: "http://europa:8080/health"
  required_services:
    - "traefik"
    - "plex"
EOF

    cat > "$DEPLOYMENT_CONFIG_DIR/servers/io.yml" << 'EOF'
# IO Server Configuration - Download/Acquisition Server
name: "io"
role: "download-server"
description: "Media acquisition and download management server"

connection:
  hostname: "io"
  user: "acedanger"
  ssh_key: "~/.ssh/id_rsa"
  docker_compose_dir: "~/docker"

stacks:
  - radarr
  - sonarr
  - lidarr
  - sabnzbd
  - qbittorrent
  - prowlarr
  - overseerr

resources:
  cpu_cores: 2
  memory_gb: 4
  storage_gb: 200

monitoring:
  health_check_url: "http://io:8080/health"
  required_services:
    - "sabnzbd"
    - "radarr"
    - "sonarr"
EOF

    cat > "$DEPLOYMENT_CONFIG_DIR/servers/racknerd.yml" << 'EOF'
# Racknerd Server Configuration - Backup Server
name: "racknerd"
role: "backup-server"
description: "Backup, monitoring, and utility services server"

connection:
  hostname: "racknerd"
  user: "acedanger"
  ssh_key: "~/.ssh/id_rsa"
  docker_compose_dir: "~/docker"

stacks:
  - grafana
  - prometheus
  - uptime-kuma
  - vaultwarden
  - portainer
  - watchtower

resources:
  cpu_cores: 1
  memory_gb: 2
  storage_gb: 100

monitoring:
  health_check_url: "http://racknerd:8080/health"
  required_services:
    - "uptime-kuma"
    - "vaultwarden"
EOF

    # Create stack metadata examples
    if [ -d "$DOCKER_DIR/plex" ]; then
        cat > "$DEPLOYMENT_CONFIG_DIR/stacks/plex.yml" << 'EOF'
# Plex Stack Deployment Configuration
name: "plex"
description: "Plex Media Server"

deployment:
  servers: ["europa"]
  priority: "high"
  dependencies: ["traefik"]
  restart_policy: "unless-stopped"

health_check:
  enabled: true
  url: "http://localhost:32400/web"
  timeout: 30
  retries: 3

volumes:
  - "/mnt/media:/media:ro"
  - "/mnt/share/plex-config:/config"

environment:
  - "PLEX_UID=1000"
  - "PLEX_GID=1000"
  - "TZ=America/New_York"

backup:
  enabled: true
  schedule: "0 2 * * *"
  retention_days: 7
EOF
    fi

    echo -e "${GREEN}Deployment configuration initialized!${NC}"
    echo -e "${BLUE}Configuration files created in: $DEPLOYMENT_CONFIG_DIR${NC}"
    echo ""
    echo -e "${YELLOW}Next steps:${NC}"
    echo "1. Review and customize server configurations in $DEPLOYMENT_CONFIG_DIR/servers/"
    echo "2. Add stack metadata files for your Docker stacks"
    echo "3. Run '$0 map' to see the current mapping"
    echo "4. Test with '$0 deploy-all --dry-run'"

    log "Deployment configuration initialized"
}

# Load server configuration
load_server_config() {
    local server="$1"
    local config_file="$DEPLOYMENT_CONFIG_DIR/servers/${server}.yml"

    if [ ! -f "$config_file" ]; then
        echo -e "${RED}Error: Server configuration not found for '$server'${NC}"
        echo "Available servers:"
        ls "$DEPLOYMENT_CONFIG_DIR/servers/" 2>/dev/null | sed 's/\.yml$//' | sed 's/^/  - /'
        exit 1
    fi

    # For now, we'll parse YAML manually (could use yq if available)
    # Extract stacks list from YAML
    grep -A 50 "stacks:" "$config_file" | grep "^-" | sed 's/^- //' | sed 's/["'\'']//g' | sed 's/#.*//' | sed 's/[[:space:]]*$//'
}

# Load multi-server stacks from config
load_multi_server_stacks() {
    local config_file="$DEPLOYMENT_CONFIG_DIR/config.yml"
    if [ -f "$config_file" ]; then
        grep -A 10 "multi_server_stacks:" "$config_file" | grep "^-" | sed 's/^- //' | sed 's/["'\'']//g' | sed 's/#.*//' | sed 's/[[:space:]]*$//'
    fi
}

# Show stack-to-server mapping
show_mapping() {
    echo -e "${BLUE}=== Docker Stack to Server Mapping ===${NC}"
    echo ""

    # Show multi-server stacks first
    local multi_server_stacks=$(load_multi_server_stacks)
    if [ -n "$multi_server_stacks" ]; then
        echo -e "${YELLOW}🌐 Multi-Server Stacks (deployed to ALL servers)${NC}"
        echo "$multi_server_stacks" | while IFS= read -r stack; do
            if [ -n "$stack" ]; then
                local stack_path="$DOCKER_DIR/$stack"
                local description=""
                case "$stack" in
                    "dozzle") description="# Docker log viewer" ;;
                    "dockge") description="# Docker compose management" ;;
                    "diun") description="# Docker image update notifier" ;;
                    *) description="# Multi-server tool" ;;
                esac

                if [ -d "$stack_path" ]; then
                    echo "  ✅ $stack      $description"
                else
                    echo "  ❌ $stack      $description (not found locally)"
                fi
            fi
        done
        echo ""
    fi

    for server_file in "$DEPLOYMENT_CONFIG_DIR/servers/"*.yml; do
        if [ -f "$server_file" ]; then
            local server=$(basename "$server_file" .yml)
            local role=$(grep "role:" "$server_file" | cut -d'"' -f2 2>/dev/null || echo "Unknown")

            echo -e "${GREEN}📍 $server${NC} (${YELLOW}$role${NC})"

            # Get stacks for this server
            local stacks=$(load_server_config "$server")
            if [ -n "$stacks" ]; then
                echo "$stacks" | while IFS= read -r stack; do
                    if [ -n "$stack" ]; then
                        local stack_path="$DOCKER_DIR/$stack"
                        if [ -d "$stack_path" ]; then
                            echo "  ✅ $stack (exists)"
                        else
                            echo "  ❌ $stack (missing)"
                        fi
                    fi
                done
            else
                echo "  ${YELLOW}No stacks configured${NC}"
            fi
            echo ""
        fi
    done

    # Show unassigned stacks
    echo -e "${YELLOW}📦 Unassigned Stacks${NC}"
    local unassigned_count=0
    if [ -d "$DOCKER_DIR" ]; then
        for stack_dir in "$DOCKER_DIR"/*; do
            if [ -d "$stack_dir" ]; then
                local stack_name=$(basename "$stack_dir")
                local assigned=false

                # Check if stack is assigned to any server
                for server_file in "$DEPLOYMENT_CONFIG_DIR/servers/"*.yml; do
                    if [ -f "$server_file" ]; then
                        if grep -q -- "- $stack_name" "$server_file" 2>/dev/null; then
                            assigned=true
                            break
                        fi
                    fi
                done

                # Also check if it's a multi-server stack
                local multi_server_stacks=$(load_multi_server_stacks)
                if echo "$multi_server_stacks" | grep -q "^$stack_name$" 2>/dev/null; then
                    assigned=true
                fi

                if [ "$assigned" = false ]; then
                    echo "  🔍 $stack_name"
                    unassigned_count=$((unassigned_count + 1))
                fi
            fi
        done
    fi

    if [ "$unassigned_count" -eq 0 ]; then
        echo -e "  ${GREEN}✅ All stacks are assigned to servers${NC}"
    fi
}

# Sync environment files to server
sync_env_files() {
    local server="$1"
    local dry_run="$2"

    echo -e "${YELLOW}Syncing environment files to $server...${NC}"

    # Get stacks for this server
    local stacks=$(load_server_config "$server")

    if [ -z "$stacks" ]; then
        echo -e "${YELLOW}No stacks configured for server $server${NC}"
        return 0
    fi

    # Create temporary directory for sync
    local temp_dir=$(mktemp -d)
    local sync_count=0

    echo "$stacks" | while IFS= read -r stack; do
        if [ -n "$stack" ]; then
            local stack_path="$DOCKER_DIR/$stack"

            if [ -d "$stack_path" ]; then
                # Find .env files in stack directory
                find "$stack_path" -name "*.env" -o -name ".env*" | while IFS= read -r env_file; do
                    if [ -n "$env_file" ]; then
                        local rel_path="${env_file#$DOCKER_DIR/}"
                        local dest_dir="$temp_dir/$(dirname "$rel_path")"

                        if [ "$dry_run" = "true" ]; then
                            echo -e "${BLUE}Would sync: $rel_path${NC}"
                        else
                            mkdir -p "$dest_dir"
                            cp "$env_file" "$dest_dir/"
                            echo -e "${GREEN}✓ Prepared: $rel_path${NC}"
                            sync_count=$((sync_count + 1))
                        fi
                    fi
                done

                # Also sync docker-compose.yml
                local compose_file="$stack_path/docker-compose.yml"
                if [ -f "$compose_file" ]; then
                    local rel_path="${compose_file#$DOCKER_DIR/}"
                    local dest_dir="$temp_dir/$(dirname "$rel_path")"

                    if [ "$dry_run" = "true" ]; then
                        echo -e "${BLUE}Would sync: $rel_path${NC}"
                    else
                        mkdir -p "$dest_dir"
                        cp "$compose_file" "$dest_dir/"
                        echo -e "${GREEN}✓ Prepared: $rel_path${NC}"
                    fi
                fi
            else
                echo -e "${YELLOW}Warning: Stack directory not found: $stack_path${NC}"
            fi
        fi
    done

    if [ "$dry_run" != "true" ]; then
        # Use rsync to sync to server (assumes SSH access)
        echo -e "${YELLOW}Transferring files to $server...${NC}"

        # This would be the actual rsync command (commented for safety)
        # rsync -avz --delete "$temp_dir/" "acedanger@$server:~/docker/"

        echo -e "${GREEN}Environment sync simulation completed for $server${NC}"
        echo -e "${BLUE}Files prepared in: $temp_dir${NC}"
        echo "To actually sync, you would run:"
        echo "  rsync -avz --delete '$temp_dir/' 'acedanger@$server:~/docker/'"

        # Clean up temp directory
        # rm -rf "$temp_dir"
    fi

    log "Environment sync completed for $server - $sync_count files prepared"
}

# Deploy stacks to server
deploy_to_server() {
    local server="$1"
    local dry_run="$2"
    local force="$3"

    echo -e "${YELLOW}Deploying Docker stacks to $server...${NC}"

    # First sync environment files
    sync_env_files "$server" "$dry_run"

    if [ "$dry_run" = "true" ]; then
        echo -e "${BLUE}Dry run completed for $server${NC}"
        return 0
    fi

    # Get stacks for this server
    local stacks=$(load_server_config "$server")

    if [ -z "$stacks" ]; then
        echo -e "${YELLOW}No stacks configured for server $server${NC}"
        return 0
    fi

    echo -e "${GREEN}Stacks to deploy on $server:${NC}"
    echo "$stacks" | sed 's/^/  - /'

    # Here you would implement the actual deployment logic
    # This could involve:
    # 1. SSH to the server
    # 2. Pull the latest compose files
    # 3. Run docker-compose up -d for each stack
    # 4. Perform health checks
    # 5. Send notifications

    echo -e "${GREEN}Deployment simulation completed for $server${NC}"

    # Send notification (using your existing ntfy setup)
    if command -v curl >/dev/null 2>&1; then
        curl -s \
            -H "priority:default" \
            -H "tags:deployment,docker,$server" \
            -d "Deployed Docker stacks to $server: $(echo "$stacks" | tr '\n' ', ' | sed 's/, $//')" \
            "https://notify.peterwood.rocks/lab" >/dev/null || true
    fi

    log "Deployment completed for $server"
}

# Deploy all stacks to their designated servers
deploy_all() {
    local dry_run="$1"

    echo -e "${BLUE}=== Deploying All Stacks to Designated Servers ===${NC}"

    for server_file in "$DEPLOYMENT_CONFIG_DIR/servers/"*.yml; do
        if [ -f "$server_file" ]; then
            local server=$(basename "$server_file" .yml)
            echo ""
            deploy_to_server "$server" "$dry_run"
        fi
    done

    echo ""
    echo -e "${GREEN}All deployments completed!${NC}"
}

# Check deployment status
check_status() {
    local server="$1"

    echo -e "${BLUE}=== Deployment Status for $server ===${NC}"

    # This would check the actual status on the server
    # For now, we'll simulate it
    echo -e "${GREEN}✅ Server is reachable${NC}"
    echo -e "${GREEN}✅ Docker is running${NC}"
    echo -e "${GREEN}✅ All stacks are healthy${NC}"

    # Get stacks for this server
    local stacks=$(load_server_config "$server")
    if [ -n "$stacks" ]; then
        echo ""
        echo -e "${BLUE}Configured stacks:${NC}"
        echo "$stacks" | while IFS= read -r stack; do
            if [ -n "$stack" ]; then
                echo -e "  ${GREEN}✅${NC} $stack"
            fi
        done
    fi
}

# Main function
main() {
    local command=""
    local dry_run=false
    local force=false
    local verbose=false
    local config_only=false
    local env_only=false

    # Parse command line arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help)
                usage
                exit 0
                ;;
            -d|--dry-run)
                dry_run=true
                shift
                ;;
            -f|--force)
                force=true
                shift
                ;;
            -v|--verbose)
                verbose=true
                shift
                ;;
            --config-only)
                config_only=true
                shift
                ;;
            --env-only)
                env_only=true
                shift
                ;;
            init|map|deploy-all|status)
                command="$1"
                shift
                ;;
            deploy|sync-env|rollback)
                command="$1"
                if [[ $# -gt 1 && ! "$2" =~ ^- ]]; then
                    server="$2"
                    shift 2
                else
                    echo -e "${RED}Error: Command '$1' requires a server name${NC}"
                    exit 1
                fi
                ;;
            *)
                echo "Unknown option: $1"
                usage
                exit 1
                ;;
        esac
    done

    # Execute requested command
    case "$command" in
        init)
            init_deployment_config
            ;;
        map)
            show_mapping
            ;;
        deploy)
            deploy_to_server "$server" "$dry_run" "$force"
            ;;
        deploy-all)
            deploy_all "$dry_run"
            ;;
        sync-env)
            sync_env_files "$server" "$dry_run"
            ;;
        status)
            check_status "$server"
            ;;
        rollback)
            echo -e "${YELLOW}Rollback functionality not yet implemented${NC}"
            ;;
        "")
            echo -e "${RED}Error: No command specified${NC}"
            usage
            exit 1
            ;;
        *)
            echo -e "${RED}Error: Unknown command '$command'${NC}"
            usage
            exit 1
            ;;
    esac
}

# Run main function with all arguments
main "$@"
