#!/bin/bash
# Docker-specific functions for Mosaic Stack installer
# Handles Docker Compose operations, health checks, and service management

# shellcheck source=lib/platform.sh
source "${BASH_SOURCE[0]%/*}/platform.sh"

# ============================================================================
# Docker Compose Helpers
# ============================================================================

# Get the docker compose command (handles both plugin and standalone)
docker_compose_cmd() {
    if docker compose version &>/dev/null; then
        echo "docker compose"
    elif command -v docker-compose &>/dev/null; then
        echo "docker-compose"
    else
        return 1
    fi
}

# Run docker compose with all arguments
docker_compose() {
    local cmd
    cmd=$(docker_compose_cmd) || {
        echo -e "${ERROR}Error: Docker Compose not available${NC}"
        return 1
    }
    
    # shellcheck disable=SC2086
    $cmd "$@"
}

# ============================================================================
# Service Management
# ============================================================================

# Pull all images defined in docker-compose.yml
docker_pull_images() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    
    echo -e "${WARN}→${NC} Pulling Docker images..."
    
    if [[ -f "$env_file" ]]; then
        docker_compose -f "$compose_file" --env-file "$env_file" pull
    else
        docker_compose -f "$compose_file" pull
    fi
}

# Start services with Docker Compose
docker_compose_up() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    local profiles="${3:-}"
    local detached="${4:-true}"
    
    echo -e "${WARN}→${NC} Starting services..."
    
    local args=("-f" "$compose_file")
    
    if [[ -f "$env_file" ]]; then
        args+=("--env-file" "$env_file")
    fi
    
    if [[ -n "$profiles" ]]; then
        args+=("--profile" "$profiles")
    fi
    
    if [[ "$detached" == "true" ]]; then
        args+=("up" "-d")
    else
        args+=("up")
    fi
    
    docker_compose "${args[@]}"
}

# Stop services
docker_compose_down() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    
    echo -e "${WARN}→${NC} Stopping services..."
    
    if [[ -f "$env_file" ]]; then
        docker_compose -f "$compose_file" --env-file "$env_file" down
    else
        docker_compose -f "$compose_file" down
    fi
}

# Restart services
docker_compose_restart() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    
    echo -e "${WARN}→${NC} Restarting services..."
    
    if [[ -f "$env_file" ]]; then
        docker_compose -f "$compose_file" --env-file "$env_file" restart
    else
        docker_compose -f "$compose_file" restart
    fi
}

# ============================================================================
# Health Checks
# ============================================================================

# Wait for a container to be healthy
wait_for_healthy_container() {
    local container_name="$1"
    local timeout="${2:-120}"
    local interval="${3:-5}"
    
    echo -e "${INFO}i${NC} Waiting for ${INFO}$container_name${NC} to be healthy..."
    
    local elapsed=0
    while [[ $elapsed -lt $timeout ]]; do
        local status
        status=$(docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || echo "not_found")
        
        case "$status" in
            healthy)
                echo -e "${SUCCESS}✓${NC} $container_name is healthy"
                return 0
                ;;
            unhealthy)
                echo -e "${ERROR}✗${NC} $container_name is unhealthy"
                return 1
                ;;
            not_found)
                echo -e "${WARN}→${NC} Container $container_name not found"
                return 1
                ;;
        esac
        
        sleep "$interval"
        ((elapsed += interval))
    done
    
    echo -e "${ERROR}✗${NC} Timeout waiting for $container_name to be healthy"
    return 1
}

# Wait for multiple containers to be healthy
wait_for_healthy_containers() {
    local containers=("$@")
    local timeout="${containers[-1]}"
    unset 'containers[-1]'
    
    for container in "${containers[@]}"; do
        if ! wait_for_healthy_container "$container" "$timeout"; then
            return 1
        fi
    done
    
    return 0
}

# Wait for a service to respond on a port
wait_for_service() {
    local host="$1"
    local port="$2"
    local name="$3"
    local timeout="${4:-60}"
    
    echo -e "${INFO}i${NC} Waiting for ${INFO}$name${NC} at $host:$port..."
    
    local elapsed=0
    while [[ $elapsed -lt $timeout ]]; do
        if docker run --rm --network host alpine:latest nc -z "$host" "$port" 2>/dev/null; then
            echo -e "${SUCCESS}✓${NC} $name is responding"
            return 0
        fi
        
        sleep 2
        ((elapsed += 2))
    done
    
    echo -e "${ERROR}✗${NC} Timeout waiting for $name"
    return 1
}

# ============================================================================
# Container Status
# ============================================================================

# Get container status
get_container_status() {
    local container_name="$1"
    
    docker inspect --format='{{.State.Status}}' "$container_name" 2>/dev/null || echo "not_found"
}

# Check if container is running
is_container_running() {
    local container_name="$1"
    local status
    status=$(get_container_status "$container_name")
    [[ "$status" == "running" ]]
}

# List all Mosaic containers
list_mosaic_containers() {
    docker ps -a --filter "name=mosaic-" --format "{{.Names}}\t{{.Status}}"
}

# Get container logs
get_container_logs() {
    local container_name="$1"
    local lines="${2:-100}"
    
    docker logs --tail "$lines" "$container_name" 2>&1
}

# Tail container logs
tail_container_logs() {
    local container_name="$1"
    
    docker logs -f "$container_name"
}

# ============================================================================
# Database Operations
# ============================================================================

# Wait for PostgreSQL to be ready
wait_for_postgres() {
    local container_name="${1:-mosaic-postgres}"
    local user="${2:-mosaic}"
    local database="${3:-mosaic}"
    local timeout="${4:-60}"
    
    echo -e "${INFO}i${NC} Waiting for PostgreSQL to be ready..."
    
    local elapsed=0
    while [[ $elapsed -lt $timeout ]]; do
        if docker exec "$container_name" pg_isready -U "$user" -d "$database" &>/dev/null; then
            echo -e "${SUCCESS}✓${NC} PostgreSQL is ready"
            return 0
        fi
        
        sleep 2
        ((elapsed += 2))
    done
    
    echo -e "${ERROR}✗${NC} Timeout waiting for PostgreSQL"
    return 1
}

# Run database migrations
run_database_migrations() {
    local api_container="${1:-mosaic-api}"
    
    echo -e "${WARN}→${NC} Running database migrations..."
    
    if ! docker exec "$api_container" npx prisma migrate deploy &>/dev/null; then
        echo -e "${WARN}→${NC} Could not run migrations via API container"
        echo -e "${INFO}i${NC} Migrations will run automatically when API starts"
        return 0
    fi
    
    echo -e "${SUCCESS}✓${NC} Database migrations complete"
}

# ============================================================================
# Service URLs
# ============================================================================

# Get the URL for a service
get_service_url() {
    local service="$1"
    local port="${2:-}"
    
    local host="localhost"
    
    # Check if we're in WSL and need to use Windows host
    if is_wsl; then
        host=$(cat /etc/resolv.conf 2>/dev/null | grep nameserver | awk '{print $2}' | head -1)
    fi
    
    if [[ -n "$port" ]]; then
        echo "http://${host}:${port}"
    else
        echo "http://${host}"
    fi
}

# Get all service URLs
get_all_service_urls() {
    local env_file="${1:-.env}"
    
    declare -A urls=()
    
    if [[ -f "$env_file" ]]; then
        # shellcheck source=/dev/null
        source "$env_file"
    fi
    
    urls[web]="http://localhost:${WEB_PORT:-3000}"
    urls[api]="http://localhost:${API_PORT:-3001}"
    urls[postgres]="localhost:${POSTGRES_PORT:-5432}"
    urls[valkey]="localhost:${VALKEY_PORT:-6379}"
    
    if [[ "${OIDC_ENABLED:-false}" == "true" ]]; then
        urls[authentik]="http://localhost:${AUTHENTIK_PORT_HTTP:-9000}"
    fi
    
    if [[ "${OLLAMA_MODE:-disabled}" != "disabled" ]]; then
        urls[ollama]="http://localhost:${OLLAMA_PORT:-11434}"
    fi
    
    for service in "${!urls[@]}"; do
        echo "$service: ${urls[$service]}"
    done
}

# ============================================================================
# Docker Cleanup
# ============================================================================

# Remove unused Docker resources
docker_cleanup() {
    echo -e "${WARN}→${NC} Cleaning up unused Docker resources..."
    
    # Remove dangling images
    docker image prune -f
    
    # Remove unused networks
    docker network prune -f
    
    echo -e "${SUCCESS}✓${NC} Docker cleanup complete"
}

# Remove all Mosaic containers and volumes
docker_remove_all() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    
    echo -e "${WARN}→${NC} Removing all Mosaic containers and volumes..."
    
    if [[ -f "$env_file" ]]; then
        docker_compose -f "$compose_file" --env-file "$env_file" down -v --remove-orphans
    else
        docker_compose -f "$compose_file" down -v --remove-orphans
    fi
    
    echo -e "${SUCCESS}✓${NC} All containers and volumes removed"
}

# ============================================================================
# Docker Info
# ============================================================================

# Print Docker system info
print_docker_info() {
    echo -e "${BOLD}Docker Information:${NC}"
    echo ""
    
    echo -e "  Docker Version:"
    docker --version 2>/dev/null | sed 's/^/    /'
    
    echo ""
    echo -e "  Docker Compose:"
    docker_compose version 2>/dev/null | sed 's/^/    /'
    
    echo ""
    echo -e "  Docker Storage:"
    docker system df 2>/dev/null | sed 's/^/    /'
    
    echo ""
    echo -e "  Running Containers:"
    docker ps --format "    {{.Names}}\t{{.Status}}" 2>/dev/null | head -10
}

# ============================================================================
# Volume Management
# ============================================================================

# List all Mosaic volumes
list_mosaic_volumes() {
    docker volume ls --filter "name=mosaic" --format "{{.Name}}"
}

# Backup a Docker volume
backup_volume() {
    local volume_name="$1"
    local backup_file="${2:-${volume_name}-backup-$(date +%Y%m%d-%H%M%S).tar.gz}"
    
    echo -e "${WARN}→${NC} Backing up volume ${INFO}$volume_name${NC}..."
    
    docker run --rm \
        -v "$volume_name":/source:ro \
        -v "$(pwd)":/backup \
        alpine:latest \
        tar czf "/backup/$backup_file" -C /source .
    
    echo -e "${SUCCESS}✓${NC} Backup created: $backup_file"
}

# Restore a Docker volume
restore_volume() {
    local volume_name="$1"
    local backup_file="$2"
    
    echo -e "${WARN}→${NC} Restoring volume ${INFO}$volume_name${NC} from $backup_file..."
    
    # Create volume if it doesn't exist
    docker volume create "$volume_name" &>/dev/null || true
    
    docker run --rm \
        -v "$volume_name":/target \
        -v "$(pwd)":/backup \
        alpine:latest \
        tar xzf "/backup/$backup_file" -C /target
    
    echo -e "${SUCCESS}✓${NC} Volume restored"
}

# ============================================================================
# Network Management
# ============================================================================

# Create a Docker network if it doesn't exist
ensure_network() {
    local network_name="$1"
    
    if ! docker network inspect "$network_name" &>/dev/null; then
        echo -e "${WARN}→${NC} Creating network ${INFO}$network_name${NC}..."
        docker network create "$network_name"
        echo -e "${SUCCESS}✓${NC} Network created"
    fi
}

# Check if a network exists
network_exists() {
    local network_name="$1"
    docker network inspect "$network_name" &>/dev/null
}

# ============================================================================
# Build Operations
# ============================================================================

# Build Docker images
docker_build() {
    local compose_file="${1:-docker-compose.yml}"
    local env_file="${2:-.env}"
    local parallel="${3:-true}"
    
    echo -e "${WARN}→${NC} Building Docker images..."
    
    local args=("-f" "$compose_file")
    
    if [[ -f "$env_file" ]]; then
        args+=("--env-file" "$env_file")
    fi
    
    args+=("build")
    
    if [[ "$parallel" == "true" ]]; then
        args+=("--parallel")
    fi
    
    docker_compose "${args[@]}"
}

# Check if buildx is available
check_buildx() {
    docker buildx version &>/dev/null
}

# Set up buildx builder
setup_buildx() {
    if ! check_buildx; then
        echo -e "${WARN}→${NC} buildx not available"
        return 1
    fi
    
    # Create or use existing builder
    if ! docker buildx inspect mosaic-builder &>/dev/null; then
        echo -e "${WARN}→${NC} Creating buildx builder..."
        docker buildx create --name mosaic-builder --use
    else
        docker buildx use mosaic-builder
    fi
}
