---
# SSL Certificate Management and Renewal Playbook
# Manage Let's Encrypt certificates and other SSL certificates
# Usage: ansible-playbook playbooks/certificate_renewal.yml
# Usage: ansible-playbook playbooks/certificate_renewal.yml -e "force_renewal=true"
# Usage: ansible-playbook playbooks/certificate_renewal.yml -e "check_only=true"

- name: SSL Certificate Management and Renewal
  hosts: "{{ host_target | default('all') }}"
  gather_facts: yes
  vars:
    force_renewal: "{{ force_renewal | default(false) }}"
    check_only: "{{ check_only | default(false) }}"
    renewal_threshold_days: "{{ renewal_threshold_days | default(30) }}"
    backup_certificates: "{{ backup_certificates | default(true) }}"
    restart_services: "{{ restart_services | default(true) }}"

    # Certificate locations and services
    certificate_configs:
      atlantis:
        - name: "nginx-proxy-manager"
          cert_path: "/volume1/docker/nginx-proxy-manager/data/letsencrypt"
          domains: ["*.vish.gg", "vish.gg"]
          service: "nginx-proxy-manager"
          renewal_method: "npm"  # Nginx Proxy Manager handles this
        - name: "synology-dsm"
          cert_path: "/usr/syno/etc/certificate"
          domains: ["atlantis.vish.local"]
          service: "nginx"
          renewal_method: "synology"
      calypso:
        - name: "nginx-proxy-manager"
          cert_path: "/volume1/docker/nginx-proxy-manager/data/letsencrypt"
          domains: ["*.calypso.local"]
          service: "nginx-proxy-manager"
          renewal_method: "npm"
      homelab_vm:
        - name: "nginx"
          cert_path: "/etc/letsencrypt"
          domains: ["homelab.vish.gg"]
          service: "nginx"
          renewal_method: "certbot"
        - name: "traefik"
          cert_path: "/opt/docker/traefik/certs"
          domains: ["*.homelab.vish.gg"]
          service: "traefik"
          renewal_method: "traefik"

  tasks:
    - name: Create certificate report directory
      file:
        path: "/tmp/certificate_reports/{{ ansible_date_time.date }}"
        state: directory
        mode: '0755'
      delegate_to: localhost

    - name: Get current certificate configurations for this host
      set_fact:
        current_certificates: "{{ certificate_configs.get(inventory_hostname, []) }}"

    - name: Display certificate management plan
      debug:
        msg: |
          🔒 CERTIFICATE MANAGEMENT PLAN
          ==============================
          🖥️  Host: {{ inventory_hostname }}
          📅 Date: {{ ansible_date_time.date }}
          🔍 Check Only: {{ check_only }}
          🔄 Force Renewal: {{ force_renewal }}
          📅 Renewal Threshold: {{ renewal_threshold_days }} days
          💾 Backup Certificates: {{ backup_certificates }}

          📋 Certificates to manage: {{ current_certificates | length }}
          {% for cert in current_certificates %}
          - {{ cert.name }}: {{ cert.domains | join(', ') }}
          {% endfor %}

    - name: Check certificate expiration dates
      shell: |
        cert_info_file="/tmp/certificate_reports/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cert_info.txt"

        echo "🔒 CERTIFICATE STATUS REPORT - {{ inventory_hostname }}" > "$cert_info_file"
        echo "=================================================" >> "$cert_info_file"
        echo "Date: {{ ansible_date_time.iso8601 }}" >> "$cert_info_file"
        echo "Renewal Threshold: {{ renewal_threshold_days }} days" >> "$cert_info_file"
        echo "" >> "$cert_info_file"

        {% for cert in current_certificates %}
        echo "=== {{ cert.name }} ===" >> "$cert_info_file"
        echo "Domains: {{ cert.domains | join(', ') }}" >> "$cert_info_file"
        echo "Method: {{ cert.renewal_method }}" >> "$cert_info_file"

        # Check certificate expiration for each domain
        {% for domain in cert.domains %}
        echo "Checking {{ domain }}..." >> "$cert_info_file"

        # Try different methods to check certificate
        if command -v openssl &> /dev/null; then
          # Method 1: Check via SSL connection (if accessible)
          cert_info=$(echo | timeout 10 openssl s_client -servername {{ domain }} -connect {{ domain }}:443 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
          if [ $? -eq 0 ]; then
            echo "  SSL Connection: ✅" >> "$cert_info_file"
            echo "  $cert_info" >> "$cert_info_file"

            # Calculate days until expiration
            not_after=$(echo "$cert_info" | grep notAfter | cut -d= -f2)
            if [ -n "$not_after" ]; then
              exp_date=$(date -d "$not_after" +%s 2>/dev/null || echo "0")
              current_date=$(date +%s)
              days_left=$(( (exp_date - current_date) / 86400 ))
              echo "  Days until expiration: $days_left" >> "$cert_info_file"

              if [ $days_left -lt {{ renewal_threshold_days }} ]; then
                echo "  Status: ⚠️  RENEWAL NEEDED" >> "$cert_info_file"
              else
                echo "  Status: ✅ Valid" >> "$cert_info_file"
              fi
            fi
          else
            echo "  SSL Connection: ❌ Failed" >> "$cert_info_file"
          fi

          # Method 2: Check local certificate files
          {% if cert.cert_path %}
          if [ -d "{{ cert.cert_path }}" ]; then
            echo "  Local cert path: {{ cert.cert_path }}" >> "$cert_info_file"

            # Find certificate files
            cert_files=$(find {{ cert.cert_path }} -name "*.crt" -o -name "*.pem" -o -name "fullchain.pem" 2>/dev/null | head -5)
            if [ -n "$cert_files" ]; then
              echo "  Certificate files found:" >> "$cert_info_file"
              for cert_file in $cert_files; do
                echo "    $cert_file" >> "$cert_info_file"
                if openssl x509 -in "$cert_file" -noout -dates 2>/dev/null; then
                  local_cert_info=$(openssl x509 -in "$cert_file" -noout -dates 2>/dev/null)
                  echo "    $local_cert_info" >> "$cert_info_file"
                fi
              done
            else
              echo "  No certificate files found in {{ cert.cert_path }}" >> "$cert_info_file"
            fi
          else
            echo "  Certificate path {{ cert.cert_path }} not found" >> "$cert_info_file"
          fi
          {% endif %}
        else
          echo "  OpenSSL not available" >> "$cert_info_file"
        fi

        echo "" >> "$cert_info_file"
        {% endfor %}
        echo "" >> "$cert_info_file"
        {% endfor %}

        cat "$cert_info_file"
      register: certificate_status
      changed_when: false

    - name: Backup existing certificates
      shell: |
        backup_dir="/tmp/certificate_backups/{{ ansible_date_time.epoch }}"
        mkdir -p "$backup_dir"

        echo "Creating certificate backup..."

        {% for cert in current_certificates %}
        {% if cert.cert_path %}
        if [ -d "{{ cert.cert_path }}" ]; then
          echo "Backing up {{ cert.name }}..."
          tar -czf "$backup_dir/{{ cert.name }}_backup.tar.gz" -C "$(dirname {{ cert.cert_path }})" "$(basename {{ cert.cert_path }})" 2>/dev/null || echo "Backup failed for {{ cert.name }}"
        fi
        {% endif %}
        {% endfor %}

        echo "✅ Certificate backup created at $backup_dir"
        ls -la "$backup_dir"
      register: certificate_backup
      when:
        - backup_certificates | bool
        - not check_only | bool

    - name: Renew certificates via Certbot
      shell: |
        echo "🔄 Renewing certificates via Certbot..."

        {% if force_renewal %}
        certbot renew --force-renewal --quiet
        {% else %}
        certbot renew --quiet
        {% endif %}

        if [ $? -eq 0 ]; then
          echo "✅ Certbot renewal successful"
        else
          echo "❌ Certbot renewal failed"
          exit 1
        fi
      register: certbot_renewal
      when:
        - not check_only | bool
        - current_certificates | selectattr('renewal_method', 'equalto', 'certbot') | list | length > 0
      ignore_errors: yes

    - name: Check Nginx Proxy Manager certificates
      shell: |
        echo "🔍 Checking Nginx Proxy Manager certificates..."

        {% for cert in current_certificates %}
        {% if cert.renewal_method == 'npm' %}
        if [ -d "{{ cert.cert_path }}" ]; then
          echo "NPM certificate path exists: {{ cert.cert_path }}"

          # NPM manages certificates automatically, just check status
          find {{ cert.cert_path }} -name "*.pem" -mtime -1 | head -5 | while read cert_file; do
            echo "Recent certificate: $cert_file"
          done
        else
          echo "NPM certificate path not found: {{ cert.cert_path }}"
        fi
        {% endif %}
        {% endfor %}
      register: npm_certificate_check
      when: current_certificates | selectattr('renewal_method', 'equalto', 'npm') | list | length > 0
      changed_when: false

    - name: Restart services after certificate renewal
      ansible.builtin.command: "docker restart {{ item.service }}"
      loop: "{{ current_certificates | selectattr('service', 'defined') | list }}"
      when:
        - restart_services | bool
        - item.service is defined
      register: service_restart_result
      failed_when: false
      changed_when: service_restart_result.rc == 0
        - not check_only | bool
        - (certbot_renewal.changed | default(false)) or (force_renewal | bool)

    - name: Verify certificate renewal
      shell: |
        echo "🔍 Verifying certificate renewal..."

        verification_results=()

        {% for cert in current_certificates %}
        {% for domain in cert.domains %}
        echo "Verifying {{ domain }}..."

        if command -v openssl &> /dev/null; then
          # Check certificate via SSL connection
          cert_info=$(echo | timeout 10 openssl s_client -servername {{ domain }} -connect {{ domain }}:443 2>/dev/null | openssl x509 -noout -dates 2>/dev/null)
          if [ $? -eq 0 ]; then
            not_after=$(echo "$cert_info" | grep notAfter | cut -d= -f2)
            if [ -n "$not_after" ]; then
              exp_date=$(date -d "$not_after" +%s 2>/dev/null || echo "0")
              current_date=$(date +%s)
              days_left=$(( (exp_date - current_date) / 86400 ))

              if [ $days_left -gt {{ renewal_threshold_days }} ]; then
                echo "✅ {{ domain }}: $days_left days remaining"
                verification_results+=("{{ domain }}:OK:$days_left")
              else
                echo "⚠️  {{ domain }}: Only $days_left days remaining"
                verification_results+=("{{ domain }}:WARNING:$days_left")
              fi
            else
              echo "❌ {{ domain }}: Cannot parse expiration date"
              verification_results+=("{{ domain }}:ERROR:unknown")
            fi
          else
            echo "❌ {{ domain }}: SSL connection failed"
            verification_results+=("{{ domain }}:ERROR:connection_failed")
          fi
        else
          echo "⚠️  Cannot verify {{ domain }}: OpenSSL not available"
          verification_results+=("{{ domain }}:SKIP:no_openssl")
        fi
        {% endfor %}
        {% endfor %}

        echo ""
        echo "📊 VERIFICATION SUMMARY:"
        for result in "${verification_results[@]}"; do
          echo "$result"
        done
      register: certificate_verification
      changed_when: false

    - name: Generate certificate management report
      copy:
        content: |
          🔒 CERTIFICATE MANAGEMENT REPORT - {{ inventory_hostname }}
          ======================================================

          📅 Management Date: {{ ansible_date_time.iso8601 }}
          🖥️  Host: {{ inventory_hostname }}
          🔍 Check Only: {{ check_only }}
          🔄 Force Renewal: {{ force_renewal }}
          📅 Renewal Threshold: {{ renewal_threshold_days }} days
          💾 Backup Created: {{ backup_certificates }}

          📋 CERTIFICATES MANAGED: {{ current_certificates | length }}
          {% for cert in current_certificates %}
          - {{ cert.name }}: {{ cert.domains | join(', ') }} ({{ cert.renewal_method }})
          {% endfor %}

          📊 CERTIFICATE STATUS:
          {{ certificate_status.stdout }}

          {% if not check_only %}
          🔄 RENEWAL ACTIONS:
          {% if certbot_renewal is defined %}
          Certbot Renewal: {{ 'Success' if certbot_renewal.rc == 0 else 'Failed' }}
          {% endif %}

          {% if service_restart_result is defined %}
          Service Restarts:
          {{ service_restart_result.stdout }}
          {% endif %}

          {% if backup_certificates %}
          💾 BACKUP INFO:
          {{ certificate_backup.stdout }}
          {% endif %}
          {% endif %}

          🔍 VERIFICATION RESULTS:
          {{ certificate_verification.stdout }}

          💡 RECOMMENDATIONS:
          - Schedule regular certificate checks via cron
          - Monitor certificate expiration alerts
          - Test certificate renewal in staging environment
          - Keep certificate backups in secure location
          {% if current_certificates | selectattr('renewal_method', 'equalto', 'npm') | list | length > 0 %}
          - Nginx Proxy Manager handles automatic renewal
          {% endif %}

          ✅ CERTIFICATE MANAGEMENT COMPLETE

        dest: "/tmp/certificate_reports/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cert_report.txt"
      delegate_to: localhost

    - name: Display certificate management summary
      debug:
        msg: |

          ✅ CERTIFICATE MANAGEMENT COMPLETE - {{ inventory_hostname }}
          ====================================================

          📅 Date: {{ ansible_date_time.date }}
          🔍 Mode: {{ 'Check Only' if check_only else 'Full Management' }}
          📋 Certificates: {{ current_certificates | length }}

          {{ certificate_verification.stdout }}

          📄 Full report: /tmp/certificate_reports/{{ ansible_date_time.date }}/{{ inventory_hostname }}_cert_report.txt

          🔍 Next Steps:
          {% if check_only %}
          - Run without check_only to perform renewals
          {% endif %}
          - Schedule regular certificate monitoring
          - Set up expiration alerts
          - Test certificate functionality

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

    - name: Send certificate alerts (if configured)
      debug:
        msg: |
          📧 CERTIFICATE ALERT
          Host: {{ inventory_hostname }}
          Certificates expiring soon detected!
          Check the full report for details.
      when:
        - send_alerts | default(false) | bool
        - "'WARNING' in certificate_verification.stdout"
