---
# Update bve (lab node) Proxmox LXC containers and VMs
# Integrates features from community-scripts/ProxmoxVE update-lxcs script
# Usage: ansible-playbook -i inventory/proxmox update-containers.yml

- name: Update bve Lab LXC Containers and VMs
  hosts: bve
  gather_facts: no
  vars:
    candidate_containers:
      - 101   # chief-executive-officer
      - 103   # semaphore
      - 104   # cyberchef
      - 107   # shlink
      - 108   # paperclip
    candidate_vms:
      - 102   # mikrotik-routeros-chr
      - 106   # freepbx
      - 112   # dockploy
      - 200   # proxmox
      - 8000  # debian-13-cloud
      - 8001  # ubuntu-24.04-cloud
      - 8002  # docker-vm

  tasks:
    # ============================================================
    # PHASE 1: Discover container/VM statuses and filter templates
    # ============================================================
    - name: Check status and template status for candidate containers
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item }}"
          # Check if it's a template (skip templates)
          TEMPLATE_CHECK=$(sudo /usr/sbin/pct config "$CT_ID" 2>/dev/null | grep -i "template:" | head -1 || true)
          if echo "$TEMPLATE_CHECK" | grep -qi "template: 1"; then
            echo "TEMPLATE:$CT_ID"
          else
            # Check running status
            if sudo /usr/sbin/pct status "$CT_ID" 2>/dev/null | grep -q running; then
              echo "RUNNING:$CT_ID"
            elif sudo /usr/sbin/pct status "$CT_ID" 2>/dev/null | grep -q stopped; then
              echo "STOPPED:$CT_ID"
            else
              echo "MISSING:$CT_ID"
            fi
          fi
      loop: "{{ candidate_containers }}"
      register: container_statuses
      check_mode: no
      changed_when: false

    - name: Check status for candidate VMs
      ansible.builtin.shell:
        cmd: |
          set -e
          VM_ID="{{ item }}"
          if sudo /usr/sbin/qm status "$VM_ID" 2>/dev/null | grep -q running; then
            echo "RUNNING:$VM_ID"
          elif sudo /usr/sbin/qm status "$VM_ID" 2>/dev/null | grep -q stopped; then
            echo "STOPPED:$VM_ID"
          else
            echo "MISSING:$VM_ID"
          fi
      loop: "{{ candidate_vms }}"
      register: vm_statuses
      check_mode: no
      changed_when: false

    - name: Categorize containers by status
      ansible.builtin.set_fact:
        running_containers: "{{ container_statuses.results | map(attribute='stdout') | select('match', '^RUNNING:') | map('regex_replace', '^RUNNING:', '') | list }}"
        stopped_containers: "{{ container_statuses.results | map(attribute='stdout') | select('match', '^STOPPED:') | map('regex_replace', '^STOPPED:', '') | list }}"
        template_containers: "{{ container_statuses.results | map(attribute='stdout') | select('match', '^TEMPLATE:') | map('regex_replace', '^TEMPLATE:', '') | list }}"
        running_vms: "{{ vm_statuses.results | map(attribute='stdout') | select('match', '^RUNNING:') | map('regex_replace', '^RUNNING:', '') | list }}"
        stopped_vms: "{{ vm_statuses.results | map(attribute='stdout') | select('match', '^STOPPED:') | map('regex_replace', '^STOPPED:', '') | list }}"

    - name: Show templates being skipped
      ansible.builtin.debug:
        msg: "Skipping template containers: {{ template_containers | join(', ') }}"
      when: template_containers | length > 0

    # ============================================================
    # PHASE 2: Process running containers with OS detection
    # ============================================================
    - name: Detect OS in running containers
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item }}"
          # Detect OS by checking /etc/os-release
          OS_INFO=$(sudo /usr/sbin/pct exec "$CT_ID" -- cat /etc/os-release 2>/dev/null || echo "")
          if echo "$OS_INFO" | grep -qi "alpine"; then
            echo "alpine"
          elif echo "$OS_INFO" | grep -qi "arch"; then
            echo "arch"
          elif echo "$OS_INFO" | grep -qi "debian"; then
            echo "debian"
          elif echo "$OS_INFO" | grep -qi "ubuntu"; then
            echo "ubuntu"
          elif echo "$OS_INFO" | grep -qi "devuan"; then
            echo "devuan"
          elif echo "$OS_INFO" | grep -qi "fedora"; then
            echo "fedora"
          elif echo "$OS_INFO" | grep -qi "rocky"; then
            echo "rocky"
          elif echo "$OS_INFO" | grep -qi "centos"; then
            echo "centos"
          elif echo "$OS_INFO" | grep -qi "alma"; then
            echo "alma"
          elif echo "$OS_INFO" | grep -qi "suse"; then
            echo "opensuse"
          else
            # Default to debian/ubuntu if detection fails
            echo "unknown"
          fi
      loop: "{{ running_containers }}"
      register: container_os_detection
      check_mode: no
      changed_when: false
      when: running_containers | length > 0

    - name: Get disk usage for running containers
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item }}"
          sudo /usr/sbin/pct exec "$CT_ID" -- df -h / 2>/dev/null | tail -1 | awk '{print $5}'
      loop: "{{ running_containers }}"
      register: container_disk_usage
      check_mode: no
      changed_when: false
      when: running_containers | length > 0

    - name: Update running containers with OS-specific package manager
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          OS_TYPE="{{ item.stdout }}"
          
          echo "=== Container $CT_ID ==="
          echo "OS Type: $OS_TYPE"
          
          # Get disk usage
          DISK_USAGE=$(sudo /usr/sbin/pct exec "$CT_ID" -- df -h / 2>/dev/null | tail -1 | awk '{print $5}' || echo "unknown")
          echo "Boot disk usage: $DISK_USAGE"
          echo "Starting update..."
          
          case "$OS_TYPE" in
            alpine)
              sudo /usr/sbin/pct exec "$CT_ID" -- sh -c 'apk update && apk upgrade -a'
              ;;
            arch)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'pacman -Syyu --noconfirm'
              ;;
            fedora|rocky|centos|alma)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'dnf -y update && dnf -y upgrade'
              ;;
            ubuntu|debian|devuan)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get -yq dist-upgrade'
              ;;
            opensuse)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'zypper ref && zypper --non-interactive dup'
              ;;
            *)
              echo "Unknown OS, trying apt..."
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get -yq dist-upgrade'
              ;;
          esac
          
          echo "Update complete for container $CT_ID"
      loop: "{{ container_os_detection.results | default([]) }}"
      loop_control:
        label: "{{ item.item }}"
      register: container_update_output
      when: running_containers | length > 0

    - name: Check if reboot is required in containers
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          OS_TYPE="{{ item.stdout }}"
          
          REBOOT_NEEDED="no"
          # Check reboot-required file (Debian/Ubuntu style)
          if [ "$OS_TYPE" = "ubuntu" ] || [ "$OS_TYPE" = "debian" ] || [ "$OS_TYPE" = "devuan" ]; then
            if sudo /usr/sbin/pct exec "$CT_ID" -- test -f /var/run/reboot-required 2>/dev/null; then
              REBOOT_NEEDED="yes"
            fi
          fi
          # Check for needs-restarting (RHEL/Fedora style)
          if [ "$OS_TYPE" = "fedora" ] || [ "$OS_TYPE" = "rocky" ] || [ "$OS_TYPE" = "centos" ] || [ "$OS_TYPE" = "alma" ]; then
            if sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'which needs-restarting > /dev/null 2>&1 && needs-restarting -r' 2>/dev/null; then
              REBOOT_NEEDED="yes"
            fi
          fi
          
          echo "$CT_ID:$OS_TYPE:$REBOOT_NEEDED"
      loop: "{{ container_os_detection.results | default([]) }}"
      register: container_reboot_status
      check_mode: no
      changed_when: false
      when: running_containers | length > 0

    - name: Run patchmon-agent report if present
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'if [ -x /usr/local/bin/patchmon-agent ]; then /usr/local/bin/patchmon-agent report; fi' 2>/dev/null || true
      loop: "{{ container_os_detection.results | default([]) }}"
      register: patchmon_results
      failed_when: false
      changed_when: false
      when: running_containers | length > 0

    # ============================================================
    # PHASE 3: Process running VMs (assumed Debian/Ubuntu with apt)
    # ============================================================
    - name: Get disk usage for running VMs
      ansible.builtin.shell:
        cmd: |
          set -e
          VM_ID="{{ item }}"
          sudo /usr/sbin/qm guest exec "$VM_ID" -- bash -c "df -h / | tail -1 | awk '{print \$5}'" 2>/dev/null | jq -r '."out-data" // empty' | tr -d '\n' || echo "unknown"
      loop: "{{ running_vms }}"
      register: vm_disk_usage
      check_mode: no
      changed_when: false
      when: running_vms | length > 0

    - name: Update running VMs
      ansible.builtin.shell:
        cmd: |
          set -e
          VM_ID="{{ item }}"
          
          echo "=== VM $VM_ID ==="
          
          # Get disk usage
          DISK_USAGE=$(sudo /usr/sbin/qm guest exec "$VM_ID" -- bash -c "df -h / | tail -1 | awk '{print \$5}'" 2>/dev/null | jq -r '."out-data" // empty' | tr -d '\n' || echo "unknown")
          echo "Boot disk usage: $DISK_USAGE"
          
          sudo /usr/sbin/qm guest exec "$VM_ID" -- bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get -yq dist-upgrade'
          echo "Update complete for VM $VM_ID"
      loop: "{{ running_vms }}"
      register: vm_update_output
      when: running_vms | length > 0

    - name: Check if reboot is required in VMs
      ansible.builtin.shell:
        cmd: |
          set -e
          VM_ID="{{ item }}"
          if sudo /usr/sbin/qm guest exec "$VM_ID" -- test -f /var/run/reboot-required 2>/dev/null | grep -q '"exitcode": "OK"'; then
            echo "$VM_ID:yes"
          else
            echo "$VM_ID:no"
          fi
      loop: "{{ running_vms }}"
      register: vm_reboot_status
      check_mode: no
      changed_when: false
      when: running_vms | length > 0

    - name: Run patchmon-agent report if present on VMs
      ansible.builtin.shell:
        cmd: |
          set -e
          VM_ID="{{ item }}"
          sudo /usr/sbin/qm guest exec "$VM_ID" -- bash -c 'if [ -x /usr/local/bin/patchmon-agent ]; then /usr/local/bin/patchmon-agent report; fi' 2>/dev/null || true
      loop: "{{ running_vms }}"
      register: vm_patchmon_results
      failed_when: false
      changed_when: false
      when: running_vms | length > 0

    # ============================================================
    # PHASE 4: Process stopped containers (start, update, stop)
    # ============================================================
    - name: Start stopped containers for update
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item }}"
          echo "Starting container $CT_ID..."
          sudo /usr/sbin/pct start "$CT_ID"
          # Wait for container to be ready
          for i in {1..30}; do
            if sudo /usr/sbin/pct exec "$CT_ID" -- echo "ready" 2>/dev/null; then
              break
            fi
            sleep 2
          done
          echo "Container $CT_ID is running"
      loop: "{{ stopped_containers }}"
      register: started_containers
      when: stopped_containers | length > 0

    - name: Detect OS in newly started containers
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          OS_INFO=$(sudo /usr/sbin/pct exec "$CT_ID" -- cat /etc/os-release 2>/dev/null || echo "")
          if echo "$OS_INFO" | grep -qi "alpine"; then
            echo "alpine"
          elif echo "$OS_INFO" | grep -qi "arch"; then
            echo "arch"
          elif echo "$OS_INFO" | grep -qi "debian"; then
            echo "debian"
          elif echo "$OS_INFO" | grep -qi "ubuntu"; then
            echo "ubuntu"
          elif echo "$OS_INFO" | grep -qi "devuan"; then
            echo "devuan"
          elif echo "$OS_INFO" | grep -qi "fedora"; then
            echo "fedora"
          elif echo "$OS_INFO" | grep -qi "rocky"; then
            echo "rocky"
          elif echo "$OS_INFO" | grep -qi "centos"; then
            echo "centos"
          elif echo "$OS_INFO" | grep -qi "alma"; then
            echo "alma"
          elif echo "$OS_INFO" | grep -qi "suse"; then
            echo "opensuse"
          else
            echo "unknown"
          fi
      loop: "{{ started_containers.results | default([]) }}"
      register: stopped_container_os
      check_mode: no
      changed_when: false
      when: stopped_containers | length > 0

    - name: Update stopped containers (now running)
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          OS_TYPE="{{ item.stdout }}"
          
          echo "=== Container $CT_ID (was stopped, now updating) ==="
          echo "OS Type: $OS_TYPE"
          
          case "$OS_TYPE" in
            alpine)
              sudo /usr/sbin/pct exec "$CT_ID" -- sh -c 'apk update && apk upgrade -a'
              ;;
            arch)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'pacman -Syyu --noconfirm'
              ;;
            fedora|rocky|centos|alma)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'dnf -y update && dnf -y upgrade'
              ;;
            ubuntu|debian|devuan)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get -yq dist-upgrade'
              ;;
            opensuse)
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'zypper ref && zypper --non-interactive dup'
              ;;
            *)
              echo "Unknown OS, trying apt..."
              sudo /usr/sbin/pct exec "$CT_ID" -- bash -c 'export DEBIAN_FRONTEND=noninteractive && apt-get update -qq && apt-get -yq dist-upgrade'
              ;;
          esac
          
          echo "Update complete for container $CT_ID"
      loop: "{{ stopped_container_os.results | default([]) }}"
      loop_control:
        label: "{{ item.item }}"
      register: stopped_container_update
      when: stopped_containers | length > 0

    - name: Stop containers that were started for update
      ansible.builtin.shell:
        cmd: |
          set -e
          CT_ID="{{ item.item }}"
          echo "Stopping container $CT_ID..."
          sudo /usr/sbin/pct stop "$CT_ID"
          echo "Container $CT_ID stopped"
      loop: "{{ started_containers.results | default([]) }}"
      when: stopped_containers | length > 0

    # ============================================================
    # PHASE 5: Report summary
    # ============================================================
    - name: Build update summary
      ansible.builtin.set_fact:
        summary_report:
          host: "bve"
          templates_skipped: "{{ template_containers }}"
          running_containers_updated: "{{ running_containers }}"
          running_vms_updated: "{{ running_vms }}"
          stopped_containers_updated: "{{ stopped_containers }}"
          containers_needing_reboot: "{{ container_reboot_status.results | default([]) | selectattr('stdout', 'search', ':yes') | map(attribute='stdout') | map('regex_replace', ':(.*):yes', '') | list }}"
          vms_needing_reboot: "{{ vm_reboot_status.results | default([]) | selectattr('stdout', 'search', ':yes') | map(attribute='stdout') | map('regex_replace', ':(.*):yes', '') | list }}"

    - name: Display update summary
      ansible.builtin.debug:
        msg:
          - "=========================================="
          - "BVE UPDATE SUMMARY"
          - "=========================================="
          - "Templates skipped: {{ summary_report.templates_skipped | default([]) | join(', ') | default('None') }}"
          - "Running containers updated: {{ summary_report.running_containers_updated | default([]) | join(', ') | default('None') }}"
          - "Running VMs updated: {{ summary_report.running_vms_updated | default([]) | join(', ') | default('None') }}"
          - "Stopped containers updated (and stopped): {{ summary_report.stopped_containers_updated | default([]) | join(', ') | default('None') }}"
          - "---"
          - "Containers requiring reboot: {{ summary_report.containers_needing_reboot | default([]) | join(', ') | default('None') }}"
          - "VMs requiring reboot: {{ summary_report.vms_needing_reboot | default([]) | join(', ') | default('None') }}"
          - "=========================================="
