---
# Configuration Backup Playbook
# Backup docker-compose files, configs, and important data
# Usage: ansible-playbook playbooks/backup_configs.yml
# Usage: ansible-playbook playbooks/backup_configs.yml --limit atlantis
# Usage: ansible-playbook playbooks/backup_configs.yml -e "include_secrets=true"

- name: Backup Configurations and Important Data
  hosts: "{{ host_target | default('all') }}"
  gather_facts: yes
  vars:
    backup_base_dir: "/volume1/backups/configs"  # Synology path
    backup_local_dir: "/tmp/config_backups"



    # Configuration paths to backup per host
    config_paths:
      atlantis:
        - path: "/volume1/docker"
          name: "docker_configs"
          exclude: ["*/cache/*", "*/logs/*", "*/tmp/*"]
        - path: "/volume1/homes"
          name: "user_configs"
          exclude: ["*/Downloads/*", "*/Trash/*"]
        - path: "/etc/ssh"
          name: "ssh_config"
          exclude: ["ssh_host_*_key"]
      calypso:
        - path: "/volume1/docker"
          name: "docker_configs"
          exclude: ["*/cache/*", "*/logs/*", "*/tmp/*"]
        - path: "/etc/ssh"
          name: "ssh_config"
          exclude: ["ssh_host_*_key"]
      homelab_vm:
        - path: "/opt/docker"
          name: "docker_configs"
          exclude: ["*/cache/*", "*/logs/*", "*/tmp/*"]
        - path: "/etc/nginx"
          name: "nginx_config"
          exclude: []
        - path: "/etc/ssh"
          name: "ssh_config"
          exclude: ["ssh_host_*_key"]
      concord_nuc:
        - path: "/opt/docker"
          name: "docker_configs"
          exclude: ["*/cache/*", "*/logs/*", "*/tmp/*"]
        - path: "/etc/ssh"
          name: "ssh_config"
          exclude: ["ssh_host_*_key"]

    # Important service data directories
    service_data:
      atlantis:
        - service: "immich"
          paths: ["/volume1/docker/immich/config"]
        - service: "vaultwarden"
          paths: ["/volume1/docker/vaultwarden/data"]
        - service: "plex"
          paths: ["/volume1/docker/plex/config"]
      calypso:
        - service: "authentik"
          paths: ["/volume1/docker/authentik/config"]
        - service: "paperless"
          paths: ["/volume1/docker/paperless/config"]

  tasks:
    - name: Create backup directories
      file:
        path: "{{ item }}"
        state: directory
        mode: '0755'
      loop:
        - "{{ backup_base_dir }}/{{ inventory_hostname }}"
        - "{{ backup_local_dir }}/{{ inventory_hostname }}"
      ignore_errors: yes

    - name: Get current config paths for this host
      set_fact:
        current_configs: "{{ config_paths.get(inventory_hostname, []) }}"
        current_service_data: "{{ service_data.get(inventory_hostname, []) }}"

    - name: Display backup plan
      debug:
        msg: |
          📊 CONFIGURATION BACKUP PLAN
          =============================
          🖥️  Host: {{ inventory_hostname }}
          📅 Date: {{ ansible_date_time.date }}
          📁 Config Paths: {{ current_configs | length }}
          {% for config in current_configs %}
          - {{ config.name }}: {{ config.path }}
          {% endfor %}
          🔧 Service Data: {{ current_service_data | length }}
          {% for service in current_service_data %}
          - {{ service.service }}
          {% endfor %}
          🔐 Include Secrets: {{ include_secrets | default(false) }}
          🗜️  Compression: {{ compress_backups | default(true) }}

    - name: Create system info snapshot
      shell: |
        info_file="{{ backup_local_dir }}/{{ inventory_hostname }}/system_info_{{ ansible_date_time.epoch }}.txt"

        echo "📊 SYSTEM INFORMATION SNAPSHOT" > "$info_file"
        echo "===============================" >> "$info_file"
        echo "Host: {{ inventory_hostname }}" >> "$info_file"
        echo "Date: {{ ansible_date_time.iso8601 }}" >> "$info_file"
        echo "OS: {{ ansible_distribution }} {{ ansible_distribution_version }}" >> "$info_file"
        echo "Kernel: {{ ansible_kernel }}" >> "$info_file"
        echo "Uptime: {{ ansible_uptime_seconds | int // 86400 }} days" >> "$info_file"
        echo "" >> "$info_file"

        echo "🐳 DOCKER INFO:" >> "$info_file"
        docker --version >> "$info_file" 2>/dev/null || echo "Docker not available" >> "$info_file"
        echo "" >> "$info_file"

        echo "📦 RUNNING CONTAINERS:" >> "$info_file"
        docker ps --format "table {{ '{{' }}.Names{{ '}}' }}\t{{ '{{' }}.Image{{ '}}' }}\t{{ '{{' }}.Status{{ '}}' }}" >> "$info_file" 2>/dev/null || echo "Cannot access Docker" >> "$info_file"
        echo "" >> "$info_file"

        echo "💾 DISK USAGE:" >> "$info_file"
        df -h >> "$info_file"
        echo "" >> "$info_file"

        echo "🔧 INSTALLED PACKAGES (last 20):" >> "$info_file"
        if command -v dpkg &> /dev/null; then
          dpkg -l | tail -20 >> "$info_file"
        elif command -v rpm &> /dev/null; then
          rpm -qa | tail -20 >> "$info_file"
        fi

    - name: Backup configuration directories
      shell: |
        config_name="{{ item.name }}"
        source_path="{{ item.path }}"
        backup_file="{{ backup_local_dir }}/{{ inventory_hostname }}/${config_name}_{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}.tar"

        if [ -d "$source_path" ]; then
          echo "🔄 Backing up $config_name from $source_path..."

          # Build exclude options
          exclude_opts=""
          {% for exclude in item.exclude %}
          exclude_opts="$exclude_opts --exclude='{{ exclude }}'"
          {% endfor %}

          {% if not (include_secrets | default(false)) %}
          # Add common secret file exclusions
          exclude_opts="$exclude_opts --exclude='*.key' --exclude='*.pem' --exclude='*.p12' --exclude='*password*' --exclude='*secret*' --exclude='*.env'"
          {% endif %}

          # Create tar backup
          eval "tar -cf '$backup_file' -C '$(dirname $source_path)' $exclude_opts '$(basename $source_path)'"

          if [ $? -eq 0 ]; then
            echo "✅ $config_name backup successful"

            {% if compress_backups | default(true) %}
            gzip "$backup_file"
            backup_file="${backup_file}.gz"
            {% endif %}

            backup_size=$(du -h "$backup_file" | cut -f1)
            echo "📦 Backup size: $backup_size"

            # Copy to permanent storage
            if [ -d "{{ backup_base_dir }}/{{ inventory_hostname }}" ]; then
              cp "$backup_file" "{{ backup_base_dir }}/{{ inventory_hostname }}/"
              echo "📁 Copied to permanent storage"
            fi
          else
            echo "❌ $config_name backup failed"
          fi
        else
          echo "⚠️  $source_path does not exist, skipping $config_name"
        fi
      register: config_backups
      loop: "{{ current_configs }}"

    - name: Backup service-specific data
      shell: |
        service_name="{{ item.service }}"
        backup_file="{{ backup_local_dir }}/{{ inventory_hostname }}/service_${service_name}_{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}.tar"

        echo "🔄 Backing up $service_name service data..."

        # Create temporary file list
        temp_list="/tmp/service_${service_name}_files.txt"
        > "$temp_list"

        {% for path in item.paths %}
        if [ -d "{{ path }}" ]; then
          echo "{{ path }}" >> "$temp_list"
        fi
        {% endfor %}

        if [ -s "$temp_list" ]; then
          tar -cf "$backup_file" -T "$temp_list" {% if not (include_secrets | default(false)) %}--exclude='*.key' --exclude='*.pem' --exclude='*password*' --exclude='*secret*'{% endif %}

          if [ $? -eq 0 ]; then
            echo "✅ $service_name service data backup successful"

            {% if compress_backups | default(true) %}
            gzip "$backup_file"
            backup_file="${backup_file}.gz"
            {% endif %}

            backup_size=$(du -h "$backup_file" | cut -f1)
            echo "📦 Backup size: $backup_size"

            if [ -d "{{ backup_base_dir }}/{{ inventory_hostname }}" ]; then
              cp "$backup_file" "{{ backup_base_dir }}/{{ inventory_hostname }}/"
            fi
          else
            echo "❌ $service_name service data backup failed"
          fi
        else
          echo "⚠️  No valid paths found for $service_name"
        fi

        rm -f "$temp_list"
      register: service_backups
      loop: "{{ current_service_data }}"

    - name: Backup docker-compose files
      shell: |
        compose_backup="{{ backup_local_dir }}/{{ inventory_hostname }}/docker_compose_files_{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}.tar"

        echo "🔄 Backing up docker-compose files..."

        # Find all docker-compose files
        find /volume1 /opt /home -name "docker-compose.yml" -o -name "docker-compose.yaml" -o -name "*.yml" -path "*/docker/*" 2>/dev/null > /tmp/compose_files.txt

        if [ -s /tmp/compose_files.txt ]; then
          tar -cf "$compose_backup" -T /tmp/compose_files.txt

          if [ $? -eq 0 ]; then
            echo "✅ Docker-compose files backup successful"

            {% if compress_backups | default(true) %}
            gzip "$compose_backup"
            compose_backup="${compose_backup}.gz"
            {% endif %}

            backup_size=$(du -h "$compose_backup" | cut -f1)
            echo "📦 Backup size: $backup_size"

            if [ -d "{{ backup_base_dir }}/{{ inventory_hostname }}" ]; then
              cp "$compose_backup" "{{ backup_base_dir }}/{{ inventory_hostname }}/"
            fi
          else
            echo "❌ Docker-compose files backup failed"
          fi
        else
          echo "⚠️  No docker-compose files found"
        fi

        rm -f /tmp/compose_files.txt
      register: compose_backup

    - name: Create backup inventory
      shell: |
        inventory_file="{{ backup_local_dir }}/{{ inventory_hostname }}/backup_inventory_{{ ansible_date_time.date }}.txt"

        echo "📋 BACKUP INVENTORY" > "$inventory_file"
        echo "===================" >> "$inventory_file"
        echo "Host: {{ inventory_hostname }}" >> "$inventory_file"
        echo "Date: {{ ansible_date_time.iso8601 }}" >> "$inventory_file"
        echo "Include Secrets: {{ include_secrets | default(false) }}" >> "$inventory_file"
        echo "Compression: {{ compress_backups | default(true) }}" >> "$inventory_file"
        echo "" >> "$inventory_file"

        echo "📁 BACKUP FILES:" >> "$inventory_file"
        ls -la {{ backup_local_dir }}/{{ inventory_hostname }}/ >> "$inventory_file"

        echo "" >> "$inventory_file"
        echo "📊 BACKUP SIZES:" >> "$inventory_file"
        du -h {{ backup_local_dir }}/{{ inventory_hostname }}/* >> "$inventory_file"

        echo "" >> "$inventory_file"
        echo "🔍 BACKUP CONTENTS:" >> "$inventory_file"
        {% for config in current_configs %}
        backup_file="{{ backup_local_dir }}/{{ inventory_hostname }}/{{ config.name }}_{{ ansible_date_time.date }}_{{ ansible_date_time.hour }}{{ ansible_date_time.minute }}.tar{% if compress_backups | default(true) %}.gz{% endif %}"
        if [ -f "$backup_file" ]; then
          echo "=== {{ config.name }} ===" >> "$inventory_file"
          {% if compress_backups | default(true) %}
          tar -tzf "$backup_file" | head -20 >> "$inventory_file" 2>/dev/null || echo "Cannot list contents" >> "$inventory_file"
          {% else %}
          tar -tf "$backup_file" | head -20 >> "$inventory_file" 2>/dev/null || echo "Cannot list contents" >> "$inventory_file"
          {% endif %}
          echo "" >> "$inventory_file"
        fi
        {% endfor %}

        # Copy inventory to permanent storage
        if [ -d "{{ backup_base_dir }}/{{ inventory_hostname }}" ]; then
          cp "$inventory_file" "{{ backup_base_dir }}/{{ inventory_hostname }}/"
        fi

        cat "$inventory_file"
      register: backup_inventory

    - name: Clean up old backups
      shell: |
        echo "🧹 Cleaning up backups older than {{ backup_retention_days | default(30) }} days..."

        # Clean local backups
        find {{ backup_local_dir }}/{{ inventory_hostname }} -name "*.tar*" -mtime +{{ backup_retention_days | default(30) }} -delete
        find {{ backup_local_dir }}/{{ inventory_hostname }} -name "*.txt" -mtime +{{ backup_retention_days | default(30) }} -delete

        # Clean permanent storage backups
        if [ -d "{{ backup_base_dir }}/{{ inventory_hostname }}" ]; then
          find {{ backup_base_dir }}/{{ inventory_hostname }} -name "*.tar*" -mtime +{{ backup_retention_days | default(30) }} -delete
          find {{ backup_base_dir }}/{{ inventory_hostname }} -name "*.txt" -mtime +{{ backup_retention_days | default(30) }} -delete
        fi

        echo "✅ Cleanup complete"
      when: (backup_retention_days | default(30) | int) > 0

    - name: Display backup summary
      debug:
        msg: |

          ✅ CONFIGURATION BACKUP COMPLETE
          ================================
          🖥️  Host: {{ inventory_hostname }}
          📅 Date: {{ ansible_date_time.date }}
          📁 Config Paths: {{ current_configs | length }}
          🔧 Service Data: {{ current_service_data | length }}
          🔐 Secrets Included: {{ include_secrets | default(false) }}

          {{ backup_inventory.stdout }}

          🔍 Next Steps:
          - Verify backups: ls -la {{ backup_local_dir }}/{{ inventory_hostname }}
          - Test restore: tar -tf backup_file.tar.gz
          - Schedule regular backups via cron

          ================================
