cat > setup-bastille-wordpress-production-v6-redis.sh << 'MAINSCRIPT'
#!/bin/sh

cat << 'EOF'
==========================================
Bastille WordPress Multi-Site Deployment
Production Version (with Redis) - v6
==========================================

REQUIREMENTS:
- FreeBSD 15.0-RELEASE host system
- Will bootstrap FreeBSD 15.0-RELEASE for jails
- Fresh server or existing Bastille setup

This script will:
1. Install Bastille and Ansible
2. Configure network and firewall
3. Create database, redis, proxy, and website jails
4. Install WordPress with nginx + PHP-FPM
5. Fully configure WordPress (no manual install needed)
6. Install and enable Redis Object Cache plugin
7. Configure reverse proxy with HTTP/2 and gzip
8. Set up SSL certificates with Let's Encrypt

FEATURES:
✓ HTTP/2 enabled
✓ Gzip compression
✓ SSL certificates (staging or production)
✓ Thick or thin jails
✓ Multiple sites support
✓ Redis object caching (auto-configured)
✓ WP-CLI installed
✓ Fully automated WordPress install

NOTE: If you need to rerun this script, first destroy existing jails:
  bastille destroy -fay db0
  bastille destroy -fay redis0
  bastille destroy -fay proxy0
  bastille destroy -fay site1 site2 site3 ...

==========================================
EOF

read -p "Press Enter to continue or Ctrl+C to cancel... " dummy

# ==========================================
# INITIAL SETUP
# ==========================================

echo ""
echo "=========================================="
echo "STEP 1: Initial System Setup"
echo "=========================================="

# Update packages
echo "Updating FreeBSD packages..."
pkg update

# Install required packages
echo "Installing Bastille and Ansible..."
pkg install -y bastille py311-ansible

# Enable Bastille
echo "Enabling Bastille service..."
sysrc bastille_enable=YES

# Configure ZFS for Bastille if ZFS is enabled
if sysrc -n zfs_enable 2>/dev/null | grep -qi "yes"; then
    echo "ZFS detected, configuring Bastille for ZFS..."
    # Find the zpool name (usually 'zroot' on FreeBSD)
    ZPOOL=$(zpool list -H -o name | head -1)
    if [ -n "$ZPOOL" ]; then
        echo "Using ZFS pool: ${ZPOOL}"
        # Handle both "NO" and empty "" values
        sed -i '' 's/bastille_zfs_enable="NO"/bastille_zfs_enable="YES"/' /usr/local/etc/bastille/bastille.conf
        sed -i '' 's/bastille_zfs_enable=""/bastille_zfs_enable="YES"/' /usr/local/etc/bastille/bastille.conf
        sed -i '' "s/bastille_zfs_zpool=\"\"/bastille_zfs_zpool=\"${ZPOOL}\"/" /usr/local/etc/bastille/bastille.conf
    fi
fi

# Configure bastille0 interface
echo "Configuring bastille0 loopback interface..."
sysrc cloned_interfaces+=bastille0
sysrc ifconfig_bastille0="inet 10.0.0.1 netmask 255.255.255.0"

# Create interface now if it doesn't exist
if ! ifconfig bastille0 > /dev/null 2>&1; then
    echo "Creating bastille0 interface..."
    ifconfig lo1 create
    ifconfig lo1 name bastille0
    ifconfig bastille0 inet 10.0.0.1 netmask 255.255.255.0
fi

# Verify interface exists
if ifconfig bastille0 > /dev/null 2>&1; then
    echo "✓ bastille0 interface ready"
else
    echo "ERROR: Failed to create bastille0 interface"
    exit 1
fi

# Enable and start PF
echo "Configuring PF firewall..."
sysrc pf_enable=YES
sysrc pflog_enable=YES

# Start PF services if not running
if ! service pf status > /dev/null 2>&1; then
    service pf start
fi
if ! service pflog status > /dev/null 2>&1; then
    service pflog start
fi

# Ensure PF is actually enabled (not just loaded)
pfctl -e 2>/dev/null || true

# Start Bastille
service bastille start

echo "Initial setup complete!"

# ==========================================
# INTERACTIVE CONFIGURATION
# ==========================================

echo ""
echo "=========================================="
echo "STEP 2: Interactive Configuration"
echo "=========================================="

# Number of websites
while true; do
    read -p "How many WordPress sites do you want to create? (1-10): " NUM_SITES
    if [ "$NUM_SITES" -ge 1 ] 2>/dev/null && [ "$NUM_SITES" -le 10 ] 2>/dev/null; then
        break
    else
        echo "Please enter a number between 1 and 10"
    fi
done

# Jail type
read -p "Use thick jails? (Y/n) [default: Y]: " JAIL_TYPE_INPUT
JAIL_TYPE_INPUT=${JAIL_TYPE_INPUT:-Y}
if [ "$JAIL_TYPE_INPUT" = "Y" ] || [ "$JAIL_TYPE_INPUT" = "y" ]; then
    JAIL_TYPE_FLAG="-T"
    JAIL_TYPE_NAME="thick"
else
    JAIL_TYPE_FLAG=""
    JAIL_TYPE_NAME="thin"
fi

echo "Using ${JAIL_TYPE_NAME} jails"

# Database jail configuration
read -p "Database jail name [default: db0]: " DB_JAIL_NAME
DB_JAIL_NAME=${DB_JAIL_NAME:-db0}
read -p "Database jail IP [default: 10.0.0.10]: " DB_JAIL_IP
DB_JAIL_IP=${DB_JAIL_IP:-10.0.0.10}

# Redis jail configuration
read -p "Redis jail name [default: redis0]: " REDIS_JAIL_NAME
REDIS_JAIL_NAME=${REDIS_JAIL_NAME:-redis0}
read -p "Redis jail IP [default: 10.0.0.11]: " REDIS_JAIL_IP
REDIS_JAIL_IP=${REDIS_JAIL_IP:-10.0.0.11}

# Proxy jail configuration
read -p "Proxy jail name [default: proxy0]: " PROXY_JAIL_NAME
PROXY_JAIL_NAME=${PROXY_JAIL_NAME:-proxy0}
read -p "Proxy jail IP [default: 10.0.0.2]: " PROXY_JAIL_IP
PROXY_JAIL_IP=${PROXY_JAIL_IP:-10.0.0.2}

# Create temporary file for site configurations
SITE_CONFIG_FILE="/tmp/bastille_sites_$$.txt"
> ${SITE_CONFIG_FILE}

NEXT_IP=3
for i in $(seq 1 $NUM_SITES); do
    echo ""
    echo "--- Site ${i} Configuration ---"
    
    # Jail name with validation
    while true; do
        read -p "Site ${i} jail name [default: site${i}]: " SITE_JAIL
        SITE_JAIL=${SITE_JAIL:-site${i}}
        
        if echo "$SITE_JAIL" | grep -qE '^[a-zA-Z0-9_-]+$'; then
            break
        else
            echo "Jail name can only contain letters, numbers, underscores, and hyphens"
        fi
    done
    
    # IP address
    read -p "Site ${i} jail IP [default: 10.0.0.${NEXT_IP}]: " SITE_IP
    SITE_IP=${SITE_IP:-10.0.0.${NEXT_IP}}
    
    # Domain with validation
    while true; do
        read -p "Site ${i} domain (e.g., example.com): " SITE_DOMAIN
        if [ -n "$SITE_DOMAIN" ]; then
            break
        else
            echo "Domain cannot be empty. Please enter a valid domain."
        fi
    done
    
    # WordPress site title
    read -p "Site ${i} title [default: ${SITE_DOMAIN}]: " WP_TITLE
    WP_TITLE=${WP_TITLE:-${SITE_DOMAIN}}
    
    # WordPress admin username
    read -p "Site ${i} admin username [default: admin]: " WP_ADMIN_USER
    WP_ADMIN_USER=${WP_ADMIN_USER:-admin}
    
    # WordPress admin password
    while true; do
        read -p "Site ${i} admin password (min 8 chars): " WP_ADMIN_PASS
        if [ ${#WP_ADMIN_PASS} -ge 8 ]; then
            break
        else
            echo "Password must be at least 8 characters"
        fi
    done
    
    # WordPress admin email
    while true; do
        read -p "Site ${i} admin email: " WP_ADMIN_EMAIL
        if [ -n "$WP_ADMIN_EMAIL" ]; then
            break
        else
            echo "Email cannot be empty"
        fi
    done
    
    # Generate database credentials
    DB_NAME=$(echo "${SITE_JAIL}" | tr '-' '_')_db
    DB_USER=$(echo "${SITE_JAIL}" | tr '-' '_')_user
    DB_PASS=$(openssl rand -base64 16 | tr -d '/+=' | cut -c1-16)
    
    echo "  Database: ${DB_NAME}"
    echo "  DB User: ${DB_USER}"
    echo "  DB Password: ${DB_PASS}"
    
    # Store in file (pipe-separated for reliable parsing)
    # Format: JAIL|IP|DOMAIN|DBNAME|DBUSER|DBPASS|WP_TITLE|WP_ADMIN_USER|WP_ADMIN_PASS|WP_ADMIN_EMAIL
    echo "${SITE_JAIL}|${SITE_IP}|${SITE_DOMAIN}|${DB_NAME}|${DB_USER}|${DB_PASS}|${WP_TITLE}|${WP_ADMIN_USER}|${WP_ADMIN_PASS}|${WP_ADMIN_EMAIL}" >> ${SITE_CONFIG_FILE}
    
    NEXT_IP=$((NEXT_IP + 1))
done

# DNS Configuration
echo ""
read -p "Primary DNS server [default: 1.1.1.1]: " DNS1
DNS1=${DNS1:-1.1.1.1}
read -p "Secondary DNS server [default: 8.8.8.8]: " DNS2
DNS2=${DNS2:-8.8.8.8}

# External interface
DEFAULT_IF=$(ifconfig | grep -v lo0 | grep 'flags.*UP' | head -1 | cut -d: -f1)
read -p "External network interface [default: ${DEFAULT_IF}]: " EXT_IF
EXT_IF=${EXT_IF:-${DEFAULT_IF}}

# Certbot email
while true; do
    read -p "Email address for Let's Encrypt certificates: " CERTBOT_EMAIL
    if [ -n "$CERTBOT_EMAIL" ]; then
        break
    else
        echo "Email cannot be empty"
    fi
done

# SSL staging or production
echo ""
echo "SSL Certificate Type:"
echo "  1) Production (trusted, rate-limited - 5 per domain per week)"
echo "  2) Staging (untrusted, no rate limits - for testing)"
read -p "Choose SSL type [default: 1]: " SSL_TYPE
SSL_TYPE=${SSL_TYPE:-1}
if [ "$SSL_TYPE" = "2" ]; then
    SSL_STAGING_FLAG="--staging"
    SSL_TYPE_NAME="staging"
else
    SSL_STAGING_FLAG=""
    SSL_TYPE_NAME="production"
fi
echo "Using ${SSL_TYPE_NAME} SSL certificates"

# ==========================================
# ANSIBLE SETUP
# ==========================================

echo ""
echo "=========================================="
echo "STEP 3: Setting up Ansible structure"
echo "=========================================="

ANSIBLE_DIR=~/ansible-bastille-wordpress
rm -rf ${ANSIBLE_DIR}
mkdir -p ${ANSIBLE_DIR}
cd ${ANSIBLE_DIR}
mkdir -p roles/database/tasks roles/database/templates
mkdir -p roles/redis/tasks roles/redis/templates
mkdir -p roles/webserver/tasks roles/webserver/templates
mkdir -p roles/proxy/tasks roles/proxy/templates
mkdir -p host_vars

# Create inventory
echo "Creating inventory file..."
cat > inventory.ini << INVEOF
[bastille_host]
localhost ansible_connection=local

[database_jails]
${DB_JAIL_NAME} jail_ip=${DB_JAIL_IP}

[redis_jails]
${REDIS_JAIL_NAME} jail_ip=${REDIS_JAIL_IP}

[web_jails]
INVEOF

# Add web jails to inventory from file
REDIS_DB_INDEX=0
while IFS='|' read -r JAIL IP DOMAIN DBNAME DBUSER DBPASS WP_TITLE WP_ADMIN_USER WP_ADMIN_PASS WP_ADMIN_EMAIL; do
    echo "${JAIL} jail_ip=${IP} site_name=${JAIL} domain=${DOMAIN} db_name=${DBNAME} db_user=${DBUSER} db_pass=${DBPASS} wp_title=\"${WP_TITLE}\" wp_admin_user=${WP_ADMIN_USER} wp_admin_pass=${WP_ADMIN_PASS} wp_admin_email=${WP_ADMIN_EMAIL} redis_db=${REDIS_DB_INDEX}" >> inventory.ini
    REDIS_DB_INDEX=$((REDIS_DB_INDEX + 1))
done < ${SITE_CONFIG_FILE}

cat >> inventory.ini << INVEOF

[proxy_jails]
${PROXY_JAIL_NAME} jail_ip=${PROXY_JAIL_IP}

[all:vars]
bastille_interface=bastille0
freebsd_version=15.0-RELEASE
db_jail_ip=${DB_JAIL_IP}
redis_jail_ip=${REDIS_JAIL_IP}
proxy_jail_ip=${PROXY_JAIL_IP}
webroot_base=/usr/local/www
dns_server1=${DNS1}
dns_server2=${DNS2}
jail_type_flag=${JAIL_TYPE_FLAG}
certbot_email=${CERTBOT_EMAIL}
INVEOF

# Create main playbook
echo "Creating main playbook..."
cat > site.yml << 'PLAYBOOKEOF'
---
- name: Setup Bastille Jails for WordPress
  hosts: bastille_host
  gather_facts: yes
  
  tasks:
    - name: Check if FreeBSD release is bootstrapped
      command: bastille list releases
      register: releases
      changed_when: false

    - name: Bootstrap FreeBSD release if needed
      command: bastille bootstrap {{ freebsd_version }}
      when: freebsd_version not in releases.stdout

    # PHASE 1: Create all jails
    - name: Create database jail
      command: bastille create {{ jail_type_flag }} {{ item }} {{ freebsd_version }} {{ hostvars[item].jail_ip }} {{ bastille_interface }}
      args:
        creates: /usr/local/bastille/jails/{{ item }}
      loop: "{{ groups['database_jails'] }}"

    - name: Create redis jail
      command: bastille create {{ jail_type_flag }} {{ item }} {{ freebsd_version }} {{ hostvars[item].jail_ip }} {{ bastille_interface }}
      args:
        creates: /usr/local/bastille/jails/{{ item }}
      loop: "{{ groups['redis_jails'] }}"

    - name: Create web jails
      command: bastille create {{ jail_type_flag }} {{ item }} {{ freebsd_version }} {{ hostvars[item].jail_ip }} {{ bastille_interface }}
      args:
        creates: /usr/local/bastille/jails/{{ item }}
      loop: "{{ groups['web_jails'] }}"

    - name: Create proxy jail
      command: bastille create {{ jail_type_flag }} {{ item }} {{ freebsd_version }} {{ hostvars[item].jail_ip }} {{ bastille_interface }}
      args:
        creates: /usr/local/bastille/jails/{{ item }}
      loop: "{{ groups['proxy_jails'] }}"

    # PHASE 2: Configure resolv.conf directly in jail filesystems (before starting)
    - name: Configure DNS in database jails
      copy:
        dest: /usr/local/bastille/jails/{{ item }}/root/etc/resolv.conf
        content: |
          nameserver {{ dns_server1 }}
          nameserver {{ dns_server2 }}
      loop: "{{ groups['database_jails'] }}"

    - name: Configure DNS in redis jails
      copy:
        dest: /usr/local/bastille/jails/{{ item }}/root/etc/resolv.conf
        content: |
          nameserver {{ dns_server1 }}
          nameserver {{ dns_server2 }}
      loop: "{{ groups['redis_jails'] }}"

    - name: Configure DNS in web jails
      copy:
        dest: /usr/local/bastille/jails/{{ item }}/root/etc/resolv.conf
        content: |
          nameserver {{ dns_server1 }}
          nameserver {{ dns_server2 }}
      loop: "{{ groups['web_jails'] }}"

    - name: Configure DNS in proxy jails
      copy:
        dest: /usr/local/bastille/jails/{{ item }}/root/etc/resolv.conf
        content: |
          nameserver {{ dns_server1 }}
          nameserver {{ dns_server2 }}
      loop: "{{ groups['proxy_jails'] }}"

    # PHASE 3: Start all jails
    - name: Start all jails
      command: bastille start {{ item }}
      loop: "{{ groups['database_jails'] + groups['redis_jails'] + groups['web_jails'] + groups['proxy_jails'] }}"
      ignore_errors: yes

    # PHASE 4: Configure database jail
    - name: Install MySQL in database jail
      command: bastille pkg {{ item }} install -y mysql80-server
      loop: "{{ groups['database_jails'] }}"

    - name: Enable MySQL in database jail
      command: bastille sysrc {{ item }} mysql_enable=YES
      loop: "{{ groups['database_jails'] }}"

    - name: Check if MySQL data directory exists
      command: bastille cmd {{ item }} test -d /var/db/mysql/mysql
      loop: "{{ groups['database_jails'] }}"
      register: mysql_data_exists
      ignore_errors: yes
      changed_when: false

    - name: Create temporary my.cnf on host
      copy:
        dest: /tmp/my.cnf.{{ item.item }}
        content: |
          [mysqld]
          bind-address = {{ hostvars[item.item].jail_ip }}
          socket = /tmp/mysql.sock
          lower_case_table_names = 0
          
          [client]
          socket = /tmp/mysql.sock
      loop: "{{ mysql_data_exists.results }}"
      when: item.rc != 0
      loop_control:
        label: "{{ item.item }}"

    - name: Copy my.cnf to jail
      command: bastille cp {{ item.item }} /tmp/my.cnf.{{ item.item }} /usr/local/etc/mysql/my.cnf
      loop: "{{ mysql_data_exists.results }}"
      when: item.rc != 0
      loop_control:
        label: "{{ item.item }}"

    - name: Remove temporary my.cnf from host
      file:
        path: /tmp/my.cnf.{{ item.item }}
        state: absent
      loop: "{{ mysql_data_exists.results }}"
      when: item.rc != 0
      loop_control:
        label: "{{ item.item }}"

    - name: Initialize MySQL database
      command: bastille cmd {{ item.item }} /usr/local/libexec/mysqld --initialize-insecure --user=mysql
      loop: "{{ mysql_data_exists.results }}"
      when: item.rc != 0
      loop_control:
        label: "{{ item.item }}"

    - name: Start MySQL service
      command: bastille service {{ item }} mysql-server start
      loop: "{{ groups['database_jails'] }}"
      ignore_errors: yes

    - name: Wait for MySQL to start
      pause:
        seconds: 10

    - name: Create databases for web jails
      command: >
        bastille cmd {{ groups['database_jails'][0] }}
        mysql -u root -e "CREATE DATABASE IF NOT EXISTS {{ hostvars[item].db_name }} 
        CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
      loop: "{{ groups['web_jails'] }}"

    - name: Create database users
      command: >
        bastille cmd {{ groups['database_jails'][0] }}
        mysql -u root -e "CREATE USER IF NOT EXISTS '{{ hostvars[item].db_user }}'@'{{ hostvars[item].jail_ip }}' 
        IDENTIFIED BY '{{ hostvars[item].db_pass }}';"
      loop: "{{ groups['web_jails'] }}"
      ignore_errors: yes

    - name: Grant privileges
      command: >
        bastille cmd {{ groups['database_jails'][0] }}
        mysql -u root -e "GRANT ALL PRIVILEGES ON {{ hostvars[item].db_name }}.* 
        TO '{{ hostvars[item].db_user }}'@'{{ hostvars[item].jail_ip }}';"
      loop: "{{ groups['web_jails'] }}"

    - name: Flush privileges
      command: bastille cmd {{ groups['database_jails'][0] }} mysql -u root -e "FLUSH PRIVILEGES;"

    # PHASE 5: Configure redis jail
    - name: Configure redis jail
      include_role:
        name: redis
      vars:
        jail_name: "{{ item }}"
        jail_ip: "{{ hostvars[item].jail_ip }}"
      loop: "{{ groups['redis_jails'] }}"

    # PHASE 6: Configure web jails
    - name: Configure web jails
      include_role:
        name: webserver
      vars:
        jail_name: "{{ item }}"
        jail_ip: "{{ hostvars[item].jail_ip }}"
        site_name: "{{ hostvars[item].site_name }}"
        domain: "{{ hostvars[item].domain }}"
        db_name: "{{ hostvars[item].db_name }}"
        db_user: "{{ hostvars[item].db_user }}"
        db_pass: "{{ hostvars[item].db_pass }}"
        wp_title: "{{ hostvars[item].wp_title }}"
        wp_admin_user: "{{ hostvars[item].wp_admin_user }}"
        wp_admin_pass: "{{ hostvars[item].wp_admin_pass }}"
        wp_admin_email: "{{ hostvars[item].wp_admin_email }}"
        redis_db: "{{ hostvars[item].redis_db }}"
      loop: "{{ groups['web_jails'] }}"

    # PHASE 7: Configure proxy jail
    - name: Configure proxy jail
      include_role:
        name: proxy
      vars:
        jail_name: "{{ item }}"
        jail_ip: "{{ hostvars[item].jail_ip }}"
      loop: "{{ groups['proxy_jails'] }}"
PLAYBOOKEOF

# Create redis role
echo "Creating redis role..."
cat > roles/redis/tasks/main.yml << 'REDISEOF'
---
- name: Install Redis in redis jail
  command: bastille pkg {{ jail_name }} install -y redis vim

- name: Enable Redis in redis jail
  command: bastille sysrc {{ jail_name }} redis_enable=YES

- name: Create Redis config on host
  copy:
    dest: /tmp/redis.conf.{{ jail_name }}
    content: |
      # Redis configuration for WordPress object caching
      bind {{ jail_ip }}
      port 6379
      daemonize yes
      pidfile /var/run/redis/redis.pid
      loglevel notice
      logfile /var/log/redis/redis.log
      databases 16
      
      # Snapshotting (persistence) - optional for cache
      # Disabled by default since this is primarily for caching
      save ""
      
      # Memory management
      maxmemory 256mb
      maxmemory-policy allkeys-lru
      
      # Append only file - disabled for cache use
      appendonly no
      
      # Security - protected mode off since we're binding to specific IP
      protected-mode no
      
      # Connection settings
      timeout 0
      tcp-keepalive 300

- name: Copy Redis config to jail
  command: bastille cp {{ jail_name }} /tmp/redis.conf.{{ jail_name }} /usr/local/etc/redis.conf

- name: Remove temporary Redis config from host
  file:
    path: /tmp/redis.conf.{{ jail_name }}
    state: absent

- name: Create Redis run directory
  command: bastille cmd {{ jail_name }} mkdir -p /var/run/redis

- name: Set Redis run directory permissions
  command: bastille cmd {{ jail_name }} chown redis:redis /var/run/redis

- name: Create Redis log directory
  command: bastille cmd {{ jail_name }} mkdir -p /var/log/redis

- name: Set Redis log directory permissions
  command: bastille cmd {{ jail_name }} chown redis:redis /var/log/redis

- name: Start Redis service
  shell: bastille service {{ jail_name }} redis start &
  async: 30
  poll: 5
  ignore_errors: yes

- name: Wait for Redis to start
  pause:
    seconds: 3

- name: Verify Redis is running
  command: bastille cmd {{ jail_name }} redis-cli ping
  register: redis_ping
  retries: 3
  delay: 2
  until: redis_ping.rc == 0
  ignore_errors: yes
REDISEOF

# Create webserver role
echo "Creating webserver role..."
cat > roles/webserver/tasks/main.yml << 'WEBEOF'
---
- name: Install packages in web jail
  command: >
    bastille pkg {{ jail_name }} install -y 
    nginx php84 php84-mysqli php84-curl php84-gd 
    php84-zip php84-zlib php84-pecl-redis php84-filter php84-ctype php84-phar php84-mbstring php84-xml php84-session php84-iconv vim

- name: Enable nginx
  command: bastille sysrc {{ jail_name }} nginx_enable=YES

- name: Enable PHP-FPM
  command: bastille sysrc {{ jail_name }} php_fpm_enable=YES

- name: Create webroot directory
  command: bastille cmd {{ jail_name }} mkdir -p {{ webroot_base }}/{{ site_name }}

- name: Download WordPress
  command: >
    bastille cmd {{ jail_name }}
    fetch -o /tmp/latest.tar.gz https://wordpress.org/latest.tar.gz

- name: Extract WordPress
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && 
    tar -xzf /tmp/latest.tar.gz && 
    mv wordpress/* . && 
    rmdir wordpress && 
    rm /tmp/latest.tar.gz'

- name: Copy nginx config template
  template:
    src: nginx.conf.j2
    dest: /tmp/nginx_{{ jail_name }}.conf

- name: Deploy nginx config to jail
  command: bastille cp {{ jail_name }} /tmp/nginx_{{ jail_name }}.conf /usr/local/etc/nginx/nginx.conf

- name: Remove temp nginx config
  file:
    path: /tmp/nginx_{{ jail_name }}.conf
    state: absent

- name: Create wp-config.php from sample
  command: >
    bastille cmd {{ jail_name }}
    cp {{ webroot_base }}/{{ site_name }}/wp-config-sample.php 
    {{ webroot_base }}/{{ site_name }}/wp-config.php

- name: Configure database name in wp-config
  command: >
    bastille cmd {{ jail_name }}
    sed -i '' "s/database_name_here/{{ db_name }}/" 
    {{ webroot_base }}/{{ site_name }}/wp-config.php

- name: Configure database user in wp-config
  command: >
    bastille cmd {{ jail_name }}
    sed -i '' "s/username_here/{{ db_user }}/" 
    {{ webroot_base }}/{{ site_name }}/wp-config.php

- name: Configure database password in wp-config
  command: >
    bastille cmd {{ jail_name }}
    sed -i '' "s/password_here/{{ db_pass }}/" 
    {{ webroot_base }}/{{ site_name }}/wp-config.php

- name: Configure database host in wp-config
  command: >
    bastille cmd {{ jail_name }}
    sed -i '' "s/localhost/{{ db_jail_ip }}/" 
    {{ webroot_base }}/{{ site_name }}/wp-config.php

- name: Create SSL and Redis config script
  copy:
    dest: /tmp/add-config-{{ jail_name }}.sh
    content: |
      #!/bin/sh
      cd {{ webroot_base }}/{{ site_name }}
      cat > /tmp/wp-new.php << 'WPEOF'
      <?php
      /* SSL and reverse proxy settings */
      if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
          $_SERVER['HTTPS'] = 'on';
      }
      define('FORCE_SSL_ADMIN', true);
      
      /* Redis object cache settings */
      define('WP_REDIS_HOST', '{{ redis_jail_ip }}');
      define('WP_REDIS_PORT', 6379);
      define('WP_REDIS_DATABASE', {{ redis_db }});
      
      WPEOF
      tail -n +2 wp-config.php >> /tmp/wp-new.php
      mv /tmp/wp-new.php wp-config.php

- name: Copy config script to jail
  command: bastille cp {{ jail_name }} /tmp/add-config-{{ jail_name }}.sh /tmp/add-config.sh

- name: Run config script
  command: bastille cmd {{ jail_name }} sh /tmp/add-config.sh

- name: Clean up config script on host
  file:
    path: /tmp/add-config-{{ jail_name }}.sh
    state: absent

- name: Clean up config script in jail
  command: bastille cmd {{ jail_name }} rm /tmp/add-config.sh

- name: Set WordPress permissions
  command: >
    bastille cmd {{ jail_name }}
    chown -R www:www {{ webroot_base }}/{{ site_name }}

- name: Start nginx
  command: bastille service {{ jail_name }} nginx start
  ignore_errors: yes

- name: Start PHP-FPM
  command: bastille service {{ jail_name }} php_fpm start
  ignore_errors: yes

- name: Download WP-CLI
  command: >
    bastille cmd {{ jail_name }}
    fetch -o /usr/local/bin/wp https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

- name: Make WP-CLI executable
  command: bastille cmd {{ jail_name }} chmod +x /usr/local/bin/wp

- name: Wait for services to be ready
  pause:
    seconds: 3

- name: Install WordPress core
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && /usr/local/bin/wp core install 
    --url="https://{{ domain }}" 
    --title="{{ wp_title }}" 
    --admin_user="{{ wp_admin_user }}" 
    --admin_password="{{ wp_admin_pass }}" 
    --admin_email="{{ wp_admin_email }}" 
    --skip-email 
    --allow-root'

- name: Install Redis Object Cache plugin
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && /usr/local/bin/wp plugin install redis-cache --activate --allow-root'

- name: Enable Redis Object Cache
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && /usr/local/bin/wp redis enable --allow-root'

- name: Set permalink structure
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && /usr/local/bin/wp rewrite structure "/%postname%/" --allow-root'

- name: Flush rewrite rules
  command: >
    bastille cmd {{ jail_name }}
    sh -c 'cd {{ webroot_base }}/{{ site_name }} && /usr/local/bin/wp rewrite flush --allow-root'

- name: Fix permissions after WP-CLI
  command: >
    bastille cmd {{ jail_name }}
    chown -R www:www {{ webroot_base }}/{{ site_name }}
WEBEOF

# Create webserver nginx template (NO GZIP - proxy handles it)
cat > roles/webserver/templates/nginx.conf.j2 << 'WEBNGINXEOF'
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       80;
        server_name  localhost;
        root   {{ webroot_base }}/{{ site_name }};
        index  index.php index.html;

        location / {
            try_files $uri $uri/ /index.php?$args;
        }

        location ~ \.php$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include        fastcgi_params;
        }
    }
}
WEBNGINXEOF

# Create proxy role
echo "Creating proxy role..."
cat > roles/proxy/tasks/main.yml << 'PROXYEOF'
---
- name: Install nginx and certbot in proxy jail
  command: bastille pkg {{ jail_name }} install -y nginx py311-certbot py311-certbot-nginx vim

- name: Enable nginx in proxy jail
  command: bastille sysrc {{ jail_name }} nginx_enable=YES

- name: Copy proxy nginx config template
  template:
    src: proxy-nginx.conf.j2
    dest: /tmp/nginx_{{ jail_name }}.conf

- name: Deploy proxy nginx config to jail
  command: bastille cp {{ jail_name }} /tmp/nginx_{{ jail_name }}.conf /usr/local/etc/nginx/nginx.conf

- name: Remove temp proxy config
  file:
    path: /tmp/nginx_{{ jail_name }}.conf
    state: absent

- name: Start nginx in proxy jail
  command: bastille service {{ jail_name }} nginx start
  ignore_errors: yes
PROXYEOF

# Create proxy nginx template with HTTP/2 and GZIP properly configured
cat > roles/proxy/templates/proxy-nginx.conf.j2 << 'PROXYNGINXEOF'
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    
    # Gzip compression - proxy compresses responses from backends
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
    gzip_disable "msie6";

{% for host in groups['web_jails'] %}
    # {{ hostvars[host].site_name }}
    server {
        listen 80;
        http2 on;
        server_name {{ hostvars[host].domain }};
        
        location / {
            proxy_pass http://{{ hostvars[host].jail_ip }};
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Port $server_port;
            
            # Allow gzip by ignoring backend cache headers
            proxy_ignore_headers Cache-Control Expires;
            
            # Proxy buffering
            proxy_buffering on;
            proxy_buffer_size 4k;
            proxy_buffers 8 4k;
            proxy_busy_buffers_size 8k;
        }
    }

{% endfor %}
}
PROXYNGINXEOF

# ==========================================
# CONFIGURE PF FIREWALL
# ==========================================

echo ""
echo "=========================================="
echo "STEP 4: Configuring PF Firewall"
echo "=========================================="

# Backup existing pf.conf
if [ -f /etc/pf.conf ]; then
    cp /etc/pf.conf /etc/pf.conf.backup.$(date +%Y%m%d_%H%M%S)
    echo "Backed up existing /etc/pf.conf"
fi

# Create new pf.conf
cat > /etc/pf.conf << PFEOF
# Bastille WordPress Multi-Site Configuration
ext_if="${EXT_IF}"
table <jails> { 10.0.0.0/24 }

# NAT for jails
nat on \$ext_if from <jails> to any -> (\$ext_if)

# Redirect HTTP/HTTPS to proxy jail
rdr pass on \$ext_if proto tcp from any to any port { 80, 443 } -> ${PROXY_JAIL_IP}

# Allow traffic between jails
pass in on bastille0 from <jails> to <jails> keep state

# Allow all outbound
pass out all keep state
PFEOF

# Reload PF and ensure it's enabled
pfctl -f /etc/pf.conf
pfctl -e 2>/dev/null || true
echo "PF firewall configured and enabled"

# ==========================================
# RUN ANSIBLE PLAYBOOK
# ==========================================

echo ""
echo "=========================================="
echo "STEP 5: Running Ansible Deployment"
echo "=========================================="

cd ${ANSIBLE_DIR}
ansible-playbook -i inventory.ini site.yml

# ==========================================
# SSL CERTIFICATE SETUP
# ==========================================

echo ""
echo "=========================================="
echo "STEP 6: SSL Certificate Setup"
echo "=========================================="

echo ""
echo "Please configure DNS A records for the following domains:"
echo "Point these domains to your server's public IP address:"
echo ""

# Read domains from file
while IFS='|' read -r JAIL IP DOMAIN DBNAME DBUSER DBPASS WP_TITLE WP_ADMIN_USER WP_ADMIN_PASS WP_ADMIN_EMAIL; do
    echo "  - ${DOMAIN}"
done < ${SITE_CONFIG_FILE}

echo ""
echo "After configuring DNS, press Enter to continue with SSL setup"
echo "Or press Ctrl+C to skip SSL configuration (you can run certbot manually later)"
read -p "Press Enter when DNS is ready... " dummy

echo ""
echo "Running certbot for each domain (${SSL_TYPE_NAME} mode)..."

SSL_SUCCESS=""
SSL_FAILED=""

# Process certificates
while IFS='|' read -r JAIL IP DOMAIN DBNAME DBUSER DBPASS WP_TITLE WP_ADMIN_USER WP_ADMIN_PASS WP_ADMIN_EMAIL; do
    echo ""
    echo "Setting up SSL for: ${DOMAIN}"
    
    if bastille cmd ${PROXY_JAIL_NAME} certbot-3.11 --nginx -d ${DOMAIN} --non-interactive --agree-tos --email ${CERTBOT_EMAIL} ${SSL_STAGING_FLAG} 2>&1; then
        echo "✓ SSL certificate obtained for ${DOMAIN}"
        SSL_SUCCESS="${SSL_SUCCESS} ${DOMAIN}"
    else
        echo "✗ Failed to obtain SSL certificate for ${DOMAIN}"
        echo "  You can run certbot manually later with:"
        echo "  bastille console ${PROXY_JAIL_NAME}"
        echo "  certbot-3.11 --nginx -d ${DOMAIN} ${SSL_STAGING_FLAG}"
        SSL_FAILED="${SSL_FAILED} ${DOMAIN}"
    fi
done < ${SITE_CONFIG_FILE}

# ==========================================
# POST-SSL: ENSURE HTTP/2 STAYS ENABLED
# ==========================================

echo ""
echo "=========================================="
echo "STEP 6.5: Ensuring HTTP/2 remains enabled"
echo "=========================================="

# Create a script to fix HTTP/2 in the jail
cat > /tmp/fix-http2.sh << 'FIXSCRIPT'
#!/bin/sh
# After certbot, ensure http2 on; is present after listen 443 ssl;
sed -i '' '/listen 443 ssl;/!b; n; /http2 on;/b; i\
        http2 on;
' /usr/local/etc/nginx/nginx.conf

nginx -t && service nginx reload
FIXSCRIPT

bastille cp ${PROXY_JAIL_NAME} /tmp/fix-http2.sh /tmp/fix-http2.sh
bastille cmd ${PROXY_JAIL_NAME} sh /tmp/fix-http2.sh
rm /tmp/fix-http2.sh

echo "HTTP/2 verified and enabled"

# ==========================================
# BACKUP CONFIGURATIONS
# ==========================================

echo ""
echo "=========================================="
echo "STEP 7: Backing up configurations"
echo "=========================================="

BACKUP_DIR="/root/bastille-wordpress-backup-$(date +%Y%m%d_%H%M%S)"
mkdir -p ${BACKUP_DIR}

# Backup structure
mkdir -p ${BACKUP_DIR}/jails/${DB_JAIL_NAME}/mysql
mkdir -p ${BACKUP_DIR}/jails/${REDIS_JAIL_NAME}/redis
mkdir -p ${BACKUP_DIR}/jails/${PROXY_JAIL_NAME}/nginx
mkdir -p ${BACKUP_DIR}/host

# Copy database jail configs
bastille cmd ${DB_JAIL_NAME} cat /usr/local/etc/mysql/my.cnf > ${BACKUP_DIR}/jails/${DB_JAIL_NAME}/mysql/my.cnf 2>/dev/null || true
bastille cmd ${DB_JAIL_NAME} cat /etc/resolv.conf > ${BACKUP_DIR}/jails/${DB_JAIL_NAME}/resolv.conf 2>/dev/null || true

# Copy redis jail configs
bastille cmd ${REDIS_JAIL_NAME} cat /usr/local/etc/redis.conf > ${BACKUP_DIR}/jails/${REDIS_JAIL_NAME}/redis/redis.conf 2>/dev/null || true
bastille cmd ${REDIS_JAIL_NAME} cat /etc/resolv.conf > ${BACKUP_DIR}/jails/${REDIS_JAIL_NAME}/resolv.conf 2>/dev/null || true

# Copy proxy jail configs
bastille cmd ${PROXY_JAIL_NAME} cat /usr/local/etc/nginx/nginx.conf > ${BACKUP_DIR}/jails/${PROXY_JAIL_NAME}/nginx/nginx.conf 2>/dev/null || true
bastille cmd ${PROXY_JAIL_NAME} cat /etc/resolv.conf > ${BACKUP_DIR}/jails/${PROXY_JAIL_NAME}/resolv.conf 2>/dev/null || true

# Copy web jail configs
while IFS='|' read -r JAIL IP DOMAIN DBNAME DBUSER DBPASS WP_TITLE WP_ADMIN_USER WP_ADMIN_PASS WP_ADMIN_EMAIL; do
    mkdir -p ${BACKUP_DIR}/jails/${JAIL}/nginx
    mkdir -p ${BACKUP_DIR}/jails/${JAIL}/wordpress
    
    bastille cmd ${JAIL} cat /usr/local/etc/nginx/nginx.conf > ${BACKUP_DIR}/jails/${JAIL}/nginx/nginx.conf 2>/dev/null || true
    bastille cmd ${JAIL} cat /usr/local/www/${JAIL}/wp-config.php > ${BACKUP_DIR}/jails/${JAIL}/wordpress/wp-config.php 2>/dev/null || true
    bastille cmd ${JAIL} cat /etc/resolv.conf > ${BACKUP_DIR}/jails/${JAIL}/resolv.conf 2>/dev/null || true
done < ${SITE_CONFIG_FILE}

# Copy host configs
cp /etc/pf.conf ${BACKUP_DIR}/host/pf.conf
cp ${ANSIBLE_DIR}/inventory.ini ${BACKUP_DIR}/inventory.ini
cp ${ANSIBLE_DIR}/site.yml ${BACKUP_DIR}/site.yml

# Create credentials file
cat > ${BACKUP_DIR}/CREDENTIALS.txt << CREDEOF
==========================================
Bastille WordPress Deployment Summary
Production Version (with Redis) - v6
==========================================
Deployment Date: $(date)

JAIL CONFIGURATION:
------------------
Database Jail: ${DB_JAIL_NAME} (${DB_JAIL_IP})
Redis Jail: ${REDIS_JAIL_NAME} (${REDIS_JAIL_IP})
Proxy Jail: ${PROXY_JAIL_NAME} (${PROXY_JAIL_IP})

WEB SITES:
----------
CREDEOF

COUNTER=1
while IFS='|' read -r JAIL IP DOMAIN DBNAME DBUSER DBPASS WP_TITLE WP_ADMIN_USER WP_ADMIN_PASS WP_ADMIN_EMAIL; do
    cat >> ${BACKUP_DIR}/CREDENTIALS.txt << CREDEOF

Site ${COUNTER}: ${DOMAIN}
  Jail Name: ${JAIL}
  Jail IP: ${IP}
  WordPress URL: https://${DOMAIN}
  WordPress Admin: https://${DOMAIN}/wp-admin
  
  WordPress Admin Credentials:
    Username: ${WP_ADMIN_USER}
    Password: ${WP_ADMIN_PASS}
    Email: ${WP_ADMIN_EMAIL}
  
  Database Configuration:
    Database Name: ${DBNAME}
    Database User: ${DBUSER}
    Database Password: ${DBPASS}
    Database Host: ${DB_JAIL_IP}
  
  Redis Configuration:
    Redis Host: ${REDIS_JAIL_IP}
    Redis Port: 6379

CREDEOF
    COUNTER=$((COUNTER + 1))
done < ${SITE_CONFIG_FILE}

cat >> ${BACKUP_DIR}/CREDENTIALS.txt << CREDEOF

NETWORK CONFIGURATION:
---------------------
External Interface: ${EXT_IF}
Bastille Network: 10.0.0.0/24
DNS Servers: ${DNS1}, ${DNS2}

SSL CERTIFICATES:
----------------
Certbot Email: ${CERTBOT_EMAIL}
Certificate Type: ${SSL_TYPE_NAME}

Successful SSL:
CREDEOF

for DOMAIN in ${SSL_SUCCESS}; do
    echo "  ✓ ${DOMAIN}" >> ${BACKUP_DIR}/CREDENTIALS.txt
done

if [ -n "${SSL_FAILED}" ]; then
    cat >> ${BACKUP_DIR}/CREDENTIALS.txt << CREDEOF

Failed SSL (run certbot manually):
CREDEOF
    for DOMAIN in ${SSL_FAILED}; do
        echo "  ✗ ${DOMAIN}" >> ${BACKUP_DIR}/CREDENTIALS.txt
    done
fi

cat >> ${BACKUP_DIR}/CREDENTIALS.txt << CREDEOF

FEATURES ENABLED:
----------------
✓ HTTP/2 enabled on proxy
✓ Gzip compression enabled
  - proxy_ignore_headers: Cache-Control Expires
  - gzip_min_length: 1024 bytes
  - Backends serve uncompressed, proxy compresses
✓ Redis object caching (auto-enabled)
  - Each site uses unique Redis database (0, 1, 2, ...)
✓ WP-CLI installed in each web jail
✓ Pretty permalinks enabled (/%postname%/)

TESTING COMMANDS:
----------------
# NOTE: Hairpin NAT - testing from the host to its own external IP won't work.
# Use the proxy jail IP (${PROXY_JAIL_IP}) with Host header instead:

# Test HTTP/2 and gzip
curl -sI -k -H "Accept-Encoding: gzip" https://${PROXY_JAIL_IP} -H "Host: DOMAIN" | grep -E "HTTP|content-encoding"

# Test all sites at once
for DOMAIN in SITE_DOMAINS; do
  echo "--- \$DOMAIN ---"
  curl -sI -k -H "Accept-Encoding: gzip" https://${PROXY_JAIL_IP} -H "Host: \$DOMAIN" | grep -E "HTTP|content-encoding"
done

# Redis tests
bastille cmd ${REDIS_JAIL_NAME} redis-cli ping
bastille cmd ${REDIS_JAIL_NAME} redis-cli info keyspace
bastille cmd ${REDIS_JAIL_NAME} redis-cli info stats | grep -E "keyspace_hits|keyspace_misses"

# Check Redis DB separation
for JAIL in SITE_JAILS; do
  grep WP_REDIS_DATABASE /usr/local/bastille/jails/\$JAIL/root/usr/local/www/\$JAIL/wp-config.php
done

BACKUP LOCATION:
---------------
All configurations backed up to: ${BACKUP_DIR}

USEFUL COMMANDS:
---------------
Check jail status: jls
Console into jail: bastille console <jail_name>
Restart jail: bastille restart <jail_name>
View logs: bastille cmd <jail_name> tail -f /var/log/nginx/error.log
Redis logs: bastille cmd ${REDIS_JAIL_NAME} tail -f /var/log/redis/redis.log

WP-CLI commands:
  bastille cmd <jail_name> sh -c 'cd /usr/local/www/<jail_name> && wp plugin list --allow-root'
  bastille cmd <jail_name> sh -c 'cd /usr/local/www/<jail_name> && wp redis status --allow-root'

Manual certbot command:
  bastille console ${PROXY_JAIL_NAME}
  certbot-3.11 --nginx -d <domain> ${SSL_STAGING_FLAG}

ADD NEW SITES:
-------------
Use the add-wordpress-site-v6.sh script to add new sites to this deployment.
It will automatically:
  - Detect existing infrastructure (db0, redis0, proxy0)
  - Find next available IP and Redis database number
  - Configure everything with Ansible (same output style as this script)

==========================================
CREDEOF

# Clean up temp file
rm -f ${SITE_CONFIG_FILE}

echo "Configurations backed up to: ${BACKUP_DIR}"

# ==========================================
# DISPLAY SUMMARY
# ==========================================

echo ""
echo "=========================================="
echo "DEPLOYMENT COMPLETE!"
echo "=========================================="
echo ""

cat ${BACKUP_DIR}/CREDENTIALS.txt

echo ""
echo "=========================================="
echo "All configurations have been saved to:"
echo "${BACKUP_DIR}"
echo "=========================================="
echo ""

MAINSCRIPT

chmod +x setup-bastille-wordpress-production-v6-redis.sh
