#!/usr/bin/env bash
# ============================================================================
# parallel-dev.sh - Parallel Development Environment for BuyerKiosk
# ============================================================================
# Spins up N parallel instances using git worktrees + Docker containers,
# all sharing your host MySQL with isolated Redis per instance.
#
# Usage: ./parallel-dev.sh <command> [options]
# Run ./parallel-dev.sh help for full usage info.
# ============================================================================

set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
METADATA_DIR="$HOME/.parallel-dev/instances"
DOCKER_IMAGE_TAG="buyerkiosk-parallel:latest"
BASE_WEB_PORT=8080
BASE_REDIS_PORT=6380
MIN_INSTANCE_NUM=3  # dev3 is the first parallel instance

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

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
info()    { echo -e "${BLUE}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn()    { echo -e "${YELLOW}[WARN]${NC} $*"; }
error()   { echo -e "${RED}[ERROR]${NC} $*" >&2; }
fatal()   { error "$*"; exit 1; }

ensure_metadata_dir() {
    mkdir -p "$METADATA_DIR"
}

# Detect docker compose command (v1 vs v2)
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
        fatal "Neither 'docker compose' nor 'docker-compose' found. Install Docker Compose."
    fi
}

# Check if Docker daemon is running
check_docker() {
    if ! docker info &>/dev/null; then
        fatal "Docker is not running. Please start Docker Desktop first."
    fi
}

# Check if a port is available
is_port_available() {
    local port=$1
    if lsof -i :"$port" &>/dev/null; then
        return 1
    fi
    return 0
}

# Find next available port starting from base
find_available_port() {
    local base=$1
    local port=$base
    while ! is_port_available "$port"; do
        port=$((port + 1))
        if [ "$port" -gt "$((base + 100))" ]; then
            fatal "Could not find available port near $base"
        fi
    done
    echo "$port"
}

# Get instance number from name (dev3 -> 3, dev4 -> 4)
name_to_num() {
    local name=$1
    echo "${name#dev}"
}

# List all existing instance names from metadata
get_existing_instances() {
    ensure_metadata_dir
    for f in "$METADATA_DIR"/*.json; do
        [ -f "$f" ] || continue
        basename "$f" .json
    done
}

# Read a field from instance metadata JSON
read_meta() {
    local name=$1
    local field=$2
    local meta_file="$METADATA_DIR/${name}.json"
    if [ ! -f "$meta_file" ]; then
        return 1
    fi
    python3 -c "import json; d=json.load(open('$meta_file')); print(d.get('$field', ''))" 2>/dev/null
}

# Get the worktree directory for an instance
get_worktree_dir() {
    local name=$1
    echo "$PARENT_DIR/buyerkiosk-web-${name}"
}

# Find next available instance name
next_instance_name() {
    local num=$MIN_INSTANCE_NUM
    while true; do
        local name="dev${num}"
        if [ ! -f "$METADATA_DIR/${name}.json" ]; then
            echo "$name"
            return
        fi
        num=$((num + 1))
    done
}

# Get actual Docker container status
get_container_status() {
    local name=$1
    local container="bk-parallel-${name}-web"
    local status
    status=$(docker inspect --format '{{.State.Status}}' "$container" 2>/dev/null || echo "not found")
    echo "$status"
}

# Generate docker-compose.parallel.yml content
generate_compose_file() {
    local name=$1
    local web_port=$2
    local redis_port=$3

    cat <<YAML
# Auto-generated by parallel-dev.sh - DO NOT EDIT
# Instance: ${name} | Web: ${web_port} | Redis: ${redis_port}

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
      args:
        PHP_VERSION: "8.3"
    image: ${DOCKER_IMAGE_TAG}
    container_name: bk-parallel-${name}-web
    ports:
      - "${web_port}:80"
    volumes:
      - ./public_html:/var/www/html/public_html
      - ./userfrosting:/var/www/html/userfrosting
      - ./.env:/var/www/html/.env:ro
      - ./logs:/var/www/html/logs
      - ./test.sh:/var/www/html/test.sh:ro
      - ./docker:/var/www/html/docker:ro
    environment:
      - DB_HOST=host.docker.internal
      - DB_USER=${DB_USER:-kiosk_db}
      - DB_PASS=${DB_PASS:-}
      - DB_NAME=${DB_NAME:-kiosk_buykiosk}
      - DB_PREFIX=${DB_PREFIX:-resaleki_}
      - DB_TEST_NAME=${DB_TEST_NAME:-kiosk_test}
      - BUYDB_NAME=${BUYDB_NAME:-kiosk_buys}
      - USERSDB_NAME=${USERSDB_NAME:-kiosk_users}
      - CONTACTDB_NAME=${CONTACTDB_NAME:-contactNumbers}
      - REDIS_URL=tcp://redis:6379
      - DEV=1
      - APP_ENV=development
      - HOME_DIR=/var/www/html
      - LOG_DIR=/var/www/html/logs/
      - COMPOSER_IGNORE_PLATFORM=1
    depends_on:
      redis:
        condition: service_started
    extra_hosts:
      - "host.docker.internal:host-gateway"

  redis:
    image: redis:7-alpine
    container_name: bk-parallel-${name}-redis
    ports:
      - "${redis_port}:6379"
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data

volumes:
  redis_data:
    driver: local
YAML
}

# Save instance metadata
save_metadata() {
    local name=$1
    local branch=$2
    local web_port=$3
    local redis_port=$4
    local worktree_dir=$5

    ensure_metadata_dir
    cat > "$METADATA_DIR/${name}.json" <<JSON
{
    "name": "${name}",
    "branch": "${branch}",
    "web_port": ${web_port},
    "redis_port": ${redis_port},
    "worktree_dir": "${worktree_dir}",
    "created_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
}
JSON
}

# Source .env file from main worktree to get DB credentials
load_env() {
    local env_file="$SCRIPT_DIR/userfrosting/.env"
    if [ -f "$env_file" ]; then
        # Parse key=value, stripping quotes and spaces around =
        while IFS= read -r line || [ -n "$line" ]; do
            # Skip comments and empty lines
            [[ "$line" =~ ^[[:space:]]*# ]] && continue
            [[ -z "$line" ]] && continue
            # Extract key and value
            if [[ "$line" =~ ^([A-Za-z_][A-Za-z0-9_]*)[[:space:]]*=[[:space:]]*(.*) ]]; then
                local key="${BASH_REMATCH[1]}"
                local val="${BASH_REMATCH[2]}"
                # Strip surrounding quotes
                val="${val#\"}"
                val="${val%\"}"
                val="${val#\'}"
                val="${val%\'}"
                # Only export DB-related vars needed for compose generation
                case "$key" in
                    DB_USER|DB_PASS|DB_NAME|DB_PREFIX|DB_TEST_NAME|BUYDB_NAME|USERSDB_NAME|CONTACTDB_NAME)
                        export "$key=$val"
                        ;;
                esac
            fi
        done < "$env_file"
    fi
}

# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------

cmd_create() {
    local branch=""
    local name=""
    local web_port=""
    local redis_port=""

    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case $1 in
            --name=*) name="${1#*=}"; shift ;;
            --name)   name="$2"; shift 2 ;;
            --port=*) web_port="${1#*=}"; shift ;;
            --port)   web_port="$2"; shift 2 ;;
            --help|-h) cmd_help_create; exit 0 ;;
            -*) fatal "Unknown option: $1" ;;
            *)
                if [ -z "$branch" ]; then
                    branch="$1"
                else
                    fatal "Unexpected argument: $1"
                fi
                shift
                ;;
        esac
    done

    [ -z "$branch" ] && fatal "Branch name required. Usage: $0 create <branch> [--name=dev3] [--port=8083]"

    check_docker

    # Check if branch exists
    if ! git -C "$SCRIPT_DIR" rev-parse --verify "$branch" &>/dev/null; then
        echo -e "${YELLOW}Branch '$branch' does not exist.${NC}"
        read -rp "Create it from HEAD? [y/N] " answer
        if [[ "$answer" =~ ^[Yy] ]]; then
            git -C "$SCRIPT_DIR" branch "$branch"
            success "Created branch '$branch' from HEAD"
        else
            fatal "Branch '$branch' does not exist. Create it first or use an existing branch."
        fi
    fi

    # Auto-assign name if not provided
    if [ -z "$name" ]; then
        name=$(next_instance_name)
    fi

    # Validate name format
    if [[ ! "$name" =~ ^dev[0-9]+$ ]]; then
        fatal "Instance name must match 'devN' pattern (e.g., dev3, dev4). Got: $name"
    fi

    # Check if instance already exists
    if [ -f "$METADATA_DIR/${name}.json" ]; then
        fatal "Instance '$name' already exists. Use './parallel-dev.sh destroy $name' first."
    fi

    local num
    num=$(name_to_num "$name")

    # Auto-assign ports if not provided
    if [ -z "$web_port" ]; then
        web_port=$(find_available_port $((BASE_WEB_PORT + num)))
    fi
    redis_port=$(find_available_port $((BASE_REDIS_PORT + num)))

    # Verify ports
    is_port_available "$web_port" || fatal "Web port $web_port is already in use"
    is_port_available "$redis_port" || fatal "Redis port $redis_port is already in use"

    local worktree_dir
    worktree_dir=$(get_worktree_dir "$name")

    echo ""
    echo -e "${BOLD}${CYAN}Creating parallel instance: ${name}${NC}"
    echo -e "  Branch:    ${GREEN}${branch}${NC}"
    echo -e "  Directory: ${worktree_dir}"
    echo -e "  Web port:  ${web_port}"
    echo -e "  Redis:     ${redis_port}"
    echo ""

    # Step 1: Create git worktree
    info "Creating git worktree..."
    if [ -d "$worktree_dir" ]; then
        fatal "Directory $worktree_dir already exists. Remove it first or choose a different name."
    fi

    # Check if branch is already checked out in another worktree
    local branch_worktree
    branch_worktree=$(git -C "$SCRIPT_DIR" worktree list --porcelain 2>/dev/null | grep -A2 "branch refs/heads/${branch}$" | head -1 | sed 's/worktree //' || echo "")
    if [ -n "$branch_worktree" ]; then
        # Branch in use - create a new branch from it for this worktree
        local worktree_branch="${name}/${branch}"
        info "Branch '$branch' is checked out in $branch_worktree"
        info "Creating worktree branch '${worktree_branch}' from '$branch'..."
        git -C "$SCRIPT_DIR" worktree add -b "$worktree_branch" "$worktree_dir" "$branch"
        success "Worktree created at $worktree_dir (branch: $worktree_branch)"
    else
        git -C "$SCRIPT_DIR" worktree add "$worktree_dir" "$branch"
        success "Worktree created at $worktree_dir"
    fi

    # Step 2: Copy .env and gitignored files from main worktree
    # The app loads .env from project root (userfrosting/../.env via vlucas/phpdotenv)
    # We copy it but patch host-specific paths to container paths
    info "Copying configuration and dependencies..."
    if [ -f "$SCRIPT_DIR/.env" ]; then
        sed \
            -e 's|HOME_DIR.*=.*|HOME_DIR = "/var/www/html"|' \
            -e 's|LOG_DIR.*=.*|LOG_DIR = "/var/www/html/logs/"|' \
            -e 's|DB_HOST.*=.*|DB_HOST = "host.docker.internal"|' \
            "$SCRIPT_DIR/.env" > "$worktree_dir/.env"
    fi
    if [ -f "$SCRIPT_DIR/userfrosting/.env" ]; then
        sed \
            -e 's|HOME_DIR.*=.*|HOME_DIR = "/var/www/html"|' \
            -e 's|LOG_DIR.*=.*|LOG_DIR = "/var/www/html/logs/"|' \
            -e 's|DB_HOST.*=.*|DB_HOST = "host.docker.internal"|' \
            "$SCRIPT_DIR/userfrosting/.env" > "$worktree_dir/userfrosting/.env"
    fi
    # composer.lock is gitignored but essential for deterministic installs
    # (without it, composer resolves fresh and may hit PHP version mismatches)
    if [ -f "$SCRIPT_DIR/userfrosting/composer.lock" ]; then
        cp "$SCRIPT_DIR/userfrosting/composer.lock" "$worktree_dir/userfrosting/composer.lock"
    fi
    success "Config and lock files copied"

    # Step 3: Copy untracked files needed for Docker build
    # The docker/ dir has PHP config and entrypoint referenced by Dockerfile COPY
    # instructions. Some subdirs (e.g. docker/php/) are git-ignored so they won't
    # appear in the worktree checkout. We rsync the entire docker/ dir to fill gaps.
    info "Copying Docker build files..."
    mkdir -p "$worktree_dir/logs"
    if [ -d "$SCRIPT_DIR/docker" ]; then
        # Use rsync to merge (not overwrite) - fills in gitignored subdirs
        rsync -a "$SCRIPT_DIR/docker/" "$worktree_dir/docker/"
        success "Docker build files synced"
    fi
    # .dockerignore needed for efficient builds
    if [ -f "$SCRIPT_DIR/.dockerignore" ]; then
        cp "$SCRIPT_DIR/.dockerignore" "$worktree_dir/.dockerignore"
    fi

    # Step 4: Load env vars for compose generation
    load_env

    # Step 5: Generate docker-compose.parallel.yml
    info "Generating Docker Compose file..."
    generate_compose_file "$name" "$web_port" "$redis_port" > "$worktree_dir/docker-compose.parallel.yml"
    success "docker-compose.parallel.yml generated"

    # Step 6: Start containers
    info "Starting Docker containers (this may take a minute on first build)..."
    local compose_cmd
    compose_cmd=$(docker_compose_cmd)
    (cd "$worktree_dir" && $compose_cmd -f docker-compose.parallel.yml up -d --build)
    success "Containers started"

    # Step 7: Save metadata
    save_metadata "$name" "$branch" "$web_port" "$redis_port" "$worktree_dir"
    success "Metadata saved"

    # Done!
    echo ""
    echo -e "${BOLD}${GREEN}Instance '${name}' is ready!${NC}"
    echo ""
    echo -e "  ${BOLD}Local URL:${NC}   http://localhost:${web_port}"
    echo -e "  ${BOLD}Worktree:${NC}    ${worktree_dir}"
    echo -e "  ${BOLD}Branch:${NC}      ${branch}"
    echo ""
    echo -e "  ${CYAN}Quick commands:${NC}"
    echo -e "    ./parallel-dev.sh shell ${name}    # Shell into container"
    echo -e "    ./parallel-dev.sh logs ${name}     # Tail logs"
    echo -e "    ./parallel-dev.sh ngrok ${name}    # Start ngrok tunnel"
    echo -e "    ./parallel-dev.sh stop ${name}     # Stop instance"
    echo -e "    ./parallel-dev.sh destroy ${name}  # Remove everything"
    echo ""
}

cmd_list() {
    ensure_metadata_dir
    local instances
    instances=$(get_existing_instances)

    if [ -z "$instances" ]; then
        info "No parallel instances found. Create one with: ./parallel-dev.sh create <branch>"
        return
    fi

    echo ""
    echo -e "${BOLD}Parallel Development Instances${NC}"
    echo -e "────────────────────────────────────────────────────────────────"
    printf "  ${BOLD}%-10s %-18s %-8s %-12s %-10s${NC}\n" "NAME" "BRANCH" "PORT" "STATUS" "DIR"
    echo -e "────────────────────────────────────────────────────────────────"

    for name in $instances; do
        local branch web_port status worktree_dir
        branch=$(read_meta "$name" "branch")
        web_port=$(read_meta "$name" "web_port")
        worktree_dir=$(read_meta "$name" "worktree_dir")
        status=$(get_container_status "$name")

        local status_color
        case "$status" in
            running)   status_color="${GREEN}${status}${NC}" ;;
            exited)    status_color="${RED}${status}${NC}" ;;
            *)         status_color="${YELLOW}${status}${NC}" ;;
        esac

        local short_dir
        short_dir=$(basename "$worktree_dir")
        printf "  %-10s %-18s %-8s %-22b %-10s\n" "$name" "$branch" "$web_port" "$status_color" "$short_dir"
    done
    echo ""
}

cmd_status() {
    ensure_metadata_dir
    local instances
    instances=$(get_existing_instances)

    if [ -z "$instances" ]; then
        info "No parallel instances found."
        return
    fi

    echo ""
    echo -e "${BOLD}${CYAN}Parallel Dev Environment Status${NC}"
    echo ""

    for name in $instances; do
        local branch web_port redis_port status worktree_dir created_at
        branch=$(read_meta "$name" "branch")
        web_port=$(read_meta "$name" "web_port")
        redis_port=$(read_meta "$name" "redis_port")
        worktree_dir=$(read_meta "$name" "worktree_dir")
        created_at=$(read_meta "$name" "created_at")
        status=$(get_container_status "$name")

        local status_icon
        case "$status" in
            running)   status_icon="${GREEN}running${NC}" ;;
            exited)    status_icon="${RED}stopped${NC}" ;;
            *)         status_icon="${YELLOW}${status}${NC}" ;;
        esac

        echo -e "  ${BOLD}${name}${NC}  [${status_icon}]"
        echo -e "    Branch:    ${branch}"
        echo -e "    Web:       http://localhost:${web_port}"
        echo -e "    Redis:     localhost:${redis_port}"
        echo -e "    Directory: ${worktree_dir}"
        echo -e "    Created:   ${created_at}"

        # Show current git HEAD if worktree exists
        if [ -d "$worktree_dir" ]; then
            local head_ref
            head_ref=$(git -C "$worktree_dir" log --oneline -1 2>/dev/null || echo "N/A")
            echo -e "    HEAD:      ${head_ref}"
        fi
        echo ""
    done
}

cmd_start() {
    local name=$1
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 start <name>"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found. Run './parallel-dev.sh list' to see instances."

    check_docker

    local worktree_dir
    worktree_dir=$(read_meta "$name" "worktree_dir")
    [ ! -d "$worktree_dir" ] && fatal "Worktree directory not found: $worktree_dir"

    info "Starting instance '$name'..."
    local compose_cmd
    compose_cmd=$(docker_compose_cmd)
    (cd "$worktree_dir" && $compose_cmd -f docker-compose.parallel.yml up -d)

    local web_port
    web_port=$(read_meta "$name" "web_port")
    success "Instance '$name' started at http://localhost:${web_port}"
}

cmd_stop() {
    local name=$1
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 stop <name>"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local worktree_dir
    worktree_dir=$(read_meta "$name" "worktree_dir")

    info "Stopping instance '$name'..."
    local compose_cmd
    compose_cmd=$(docker_compose_cmd)
    if [ -d "$worktree_dir" ] && [ -f "$worktree_dir/docker-compose.parallel.yml" ]; then
        (cd "$worktree_dir" && $compose_cmd -f docker-compose.parallel.yml down)
    else
        # Fallback: stop containers by name
        docker stop "bk-parallel-${name}-web" "bk-parallel-${name}-redis" 2>/dev/null || true
        docker rm "bk-parallel-${name}-web" "bk-parallel-${name}-redis" 2>/dev/null || true
    fi
    success "Instance '$name' stopped"
}

cmd_destroy() {
    local name=$1
    local force=false
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 destroy <name> [--force]"

    # Check for --force flag
    shift || true
    while [[ $# -gt 0 ]]; do
        case $1 in
            --force|-f) force=true; shift ;;
            *) shift ;;
        esac
    done

    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local worktree_dir branch
    worktree_dir=$(read_meta "$name" "worktree_dir")
    branch=$(read_meta "$name" "branch")

    # Check for uncommitted changes
    if [ -d "$worktree_dir" ] && [ "$force" = false ]; then
        local changes
        changes=$(git -C "$worktree_dir" status --porcelain 2>/dev/null || echo "")
        if [ -n "$changes" ]; then
            echo ""
            warn "Worktree has uncommitted changes:"
            git -C "$worktree_dir" status --short
            echo ""
            read -rp "Destroy anyway? This will lose uncommitted work! [y/N] " answer
            if [[ ! "$answer" =~ ^[Yy] ]]; then
                info "Cancelled. Use --force to skip this check."
                return 1
            fi
        fi
    fi

    echo -e "${BOLD}Destroying instance '${name}'...${NC}"

    # Step 1: Stop and remove containers
    info "Stopping containers..."
    local compose_cmd
    compose_cmd=$(docker_compose_cmd)
    if [ -d "$worktree_dir" ] && [ -f "$worktree_dir/docker-compose.parallel.yml" ]; then
        (cd "$worktree_dir" && $compose_cmd -f docker-compose.parallel.yml down -v 2>/dev/null) || true
    fi
    # Fallback cleanup
    docker stop "bk-parallel-${name}-web" "bk-parallel-${name}-redis" 2>/dev/null || true
    docker rm "bk-parallel-${name}-web" "bk-parallel-${name}-redis" 2>/dev/null || true

    # Step 2: Remove git worktree
    if [ -d "$worktree_dir" ]; then
        info "Removing worktree..."
        git -C "$SCRIPT_DIR" worktree remove --force "$worktree_dir" 2>/dev/null || rm -rf "$worktree_dir"
    fi

    # Prune worktree references
    git -C "$SCRIPT_DIR" worktree prune 2>/dev/null || true

    # Step 3: Remove metadata
    rm -f "$METADATA_DIR/${name}.json"

    success "Instance '$name' destroyed"
    echo -e "  Branch '${branch}' still exists. Delete with: git branch -d ${branch}"
}

cmd_shell() {
    local name=$1
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 shell <name>"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local container="bk-parallel-${name}-web"
    local status
    status=$(get_container_status "$name")
    [ "$status" != "running" ] && fatal "Instance '$name' is not running (status: $status). Start it first."

    info "Connecting to $name..."
    docker exec -it "$container" bash
}

cmd_logs() {
    local name=$1
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 logs <name>"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local worktree_dir
    worktree_dir=$(read_meta "$name" "worktree_dir")

    local compose_cmd
    compose_cmd=$(docker_compose_cmd)
    (cd "$worktree_dir" && $compose_cmd -f docker-compose.parallel.yml logs -f --tail=100)
}

cmd_ngrok() {
    local name=""
    local domain=""

    while [[ $# -gt 0 ]]; do
        case $1 in
            --domain=*) domain="${1#*=}"; shift ;;
            --domain)   domain="$2"; shift 2 ;;
            -*) fatal "Unknown option: $1" ;;
            *)
                if [ -z "$name" ]; then
                    name="$1"
                fi
                shift
                ;;
        esac
    done

    [ -z "$name" ] && fatal "Instance name required. Usage: $0 ngrok <name> [--domain=dev3.buyerkiosk.com]"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local web_port
    web_port=$(read_meta "$name" "web_port")

    local status
    status=$(get_container_status "$name")
    [ "$status" != "running" ] && fatal "Instance '$name' is not running. Start it first."

    if ! command -v ngrok &>/dev/null; then
        fatal "ngrok is not installed. Install from https://ngrok.com/download"
    fi

    echo ""
    echo -e "${BOLD}Starting ngrok tunnel for ${name}${NC}"
    echo -e "  Local:  http://localhost:${web_port}"

    if [ -n "$domain" ]; then
        echo -e "  Domain: https://${domain}"
        echo ""
        ngrok http "$web_port" --domain="$domain"
    else
        echo ""
        ngrok http "$web_port"
    fi
}

cmd_test() {
    local name=$1
    shift || true
    [ -z "$name" ] && fatal "Instance name required. Usage: $0 test <name> [test-args]"
    [ ! -f "$METADATA_DIR/${name}.json" ] && fatal "Instance '$name' not found."

    local container="bk-parallel-${name}-web"
    local status
    status=$(get_container_status "$name")
    [ "$status" != "running" ] && fatal "Instance '$name' is not running. Start it first."

    info "Running tests in $name..."
    docker exec -it "$container" bash -c "cd /var/www/html && ./test.sh $*"
}

cmd_help() {
    echo ""
    echo -e "${BOLD}${CYAN}parallel-dev.sh${NC} - Parallel Development Environment for BuyerKiosk"
    echo ""
    echo -e "${BOLD}USAGE:${NC}"
    echo "    ./parallel-dev.sh <command> [options]"
    echo ""
    echo -e "${BOLD}COMMANDS:${NC}"
    echo ""
    echo -e "  ${GREEN}create${NC} <branch> [--name=devN] [--port=PORT]"
    echo "      Create a new parallel instance from a git branch."
    echo "      Auto-assigns name and ports if not specified."
    echo ""
    echo -e "  ${GREEN}list${NC}"
    echo "      Show all parallel instances in a compact table."
    echo ""
    echo -e "  ${GREEN}status${NC}"
    echo "      Show detailed status of all instances."
    echo ""
    echo -e "  ${GREEN}start${NC} <name>"
    echo "      Start a stopped instance's containers."
    echo ""
    echo -e "  ${GREEN}stop${NC} <name>"
    echo "      Stop an instance's containers (keeps worktree)."
    echo ""
    echo -e "  ${GREEN}destroy${NC} <name> [--force]"
    echo "      Remove containers, volumes, worktree, and metadata."
    echo "      Warns about uncommitted changes unless --force."
    echo ""
    echo -e "  ${GREEN}shell${NC} <name>"
    echo "      Open a bash shell in the instance's web container."
    echo ""
    echo -e "  ${GREEN}logs${NC} <name>"
    echo "      Tail container logs for an instance."
    echo ""
    echo -e "  ${GREEN}ngrok${NC} <name> [--domain=DOMAIN]"
    echo "      Start an ngrok tunnel to the instance."
    echo "      Use --domain for a custom ngrok domain."
    echo ""
    echo -e "  ${GREEN}test${NC} <name> [test-args]"
    echo "      Run test suite inside the instance's container."
    echo "      Extra args are passed to test.sh."
    echo ""
    echo -e "  ${GREEN}help${NC}"
    echo "      Show this help message."
    echo ""
    echo -e "${BOLD}ARCHITECTURE:${NC}"
    echo "    Each instance = git worktree + Docker container"
    echo "    All instances share your host MySQL (via host.docker.internal)"
    echo "    Each instance gets its own Redis for cache isolation"
    echo "    Code changes in worktrees are live immediately (volume mounts)"
    echo ""
    echo -e "${BOLD}PORT ALLOCATION:${NC}"
    echo "    main   → 80 (native Apache) / 6379 (native Redis)"
    echo "    dev3   → 8083 / 6383"
    echo "    dev4   → 8084 / 6384"
    echo "    devN   → 8080+N / 6380+N"
    echo ""
    echo -e "${BOLD}EXAMPLES:${NC}"
    echo "    ./parallel-dev.sh create feature/new-ui"
    echo "    ./parallel-dev.sh create master --name=dev3 --port=8083"
    echo "    ./parallel-dev.sh ngrok dev3 --domain=dev3.buyerkiosk.com"
    echo "    ./parallel-dev.sh test dev3 --testsuite unit"
    echo "    ./parallel-dev.sh destroy dev3"
    echo ""
    echo -e "${BOLD}FILES:${NC}"
    echo "    Worktrees:  ~/Projects/buyerkiosk-web-devN/"
    echo "    Metadata:   ~/.parallel-dev/instances/"
    echo "    Compose:    <worktree>/docker-compose.parallel.yml"
    echo ""
}

cmd_help_create() {
    echo ""
    echo -e "${BOLD}USAGE:${NC} ./parallel-dev.sh create <branch> [OPTIONS]"
    echo ""
    echo -e "${BOLD}ARGUMENTS:${NC}"
    echo "    branch    Git branch to check out (created from HEAD if doesn't exist)"
    echo ""
    echo -e "${BOLD}OPTIONS:${NC}"
    echo "    --name=devN    Instance name (default: auto-assigned dev3, dev4, ...)"
    echo "    --port=PORT    Web port (default: auto-assigned 8083, 8084, ...)"
    echo ""
}

# ---------------------------------------------------------------------------
# Main dispatcher
# ---------------------------------------------------------------------------
main() {
    local cmd="${1:-help}"
    shift || true

    case "$cmd" in
        create)  cmd_create "$@" ;;
        list|ls) cmd_list ;;
        status)  cmd_status ;;
        start)   cmd_start "${1:-}" ;;
        stop)    cmd_stop "${1:-}" ;;
        destroy) cmd_destroy "$@" ;;
        shell|sh) cmd_shell "${1:-}" ;;
        logs)    cmd_logs "${1:-}" ;;
        ngrok)   cmd_ngrok "$@" ;;
        test)    cmd_test "$@" ;;
        help|-h|--help) cmd_help ;;
        *)       error "Unknown command: $cmd"; cmd_help; exit 1 ;;
    esac
}

main "$@"
