---
# Configure HAProxy + Let's Encrypt ACME in container
# Sets up SSL termination and request routing

- name: Configure HAProxy + Let's Encrypt ACME for Veza V5 Ultra
  hosts: edge
  become: true
  gather_facts: true

  vars:
    domain: "{{ domain | default('veza.talas.fr') }}"
    acme_email: "{{ acme_email | default('ops@talas.fr') }}"
    haproxy_container: "veza-haproxy"
    webroot_port: 8888

  tasks:
    - name: Install HAProxy and ACME tools in container
      command: |
        incus exec {{ haproxy_container }} -- apt update
        incus exec {{ haproxy_container }} -- apt install -y haproxy dehydrated nginx-light
      register: install_result
      failed_when: false

    - name: Display installation result
      debug:
        var: install_result.stdout_lines

    - name: Create ACME webroot directory
      command: |
        incus exec {{ haproxy_container }} -- mkdir -p /var/www/acme-challenge
        incus exec {{ haproxy_container }} -- chown -R www-data:www-data /var/www/acme-challenge
      register: webroot_result
      failed_when: false

    - name: Configure nginx for ACME challenges
      command: |
        incus exec {{ haproxy_container }} -- tee /etc/nginx/sites-available/acme << 'EOF'
        server {
            listen 127.0.0.1:{{ webroot_port }};
            server_name _;
            root /var/www/acme-challenge;
            location /.well-known/acme-challenge/ {
                try_files $uri =404;
            }
        }
        EOF
      register: nginx_config_result
      failed_when: false

    - name: Enable nginx ACME site
      command: |
        incus exec {{ haproxy_container }} -- ln -sf /etc/nginx/sites-available/acme /etc/nginx/sites-enabled/
        incus exec {{ haproxy_container }} -- rm -f /etc/nginx/sites-enabled/default
        incus exec {{ haproxy_container }} -- systemctl restart nginx
      register: nginx_enable_result
      failed_when: false

    - name: Configure dehydrated for Let's Encrypt
      command: incus exec {{ haproxy_container }} -- bash -c 'echo "CA=\"https://acme-v02.api.letsencrypt.org/directory\"" > /etc/dehydrated/config'
      register: dehydrated_config_result
      failed_when: false

    - name: Add CHALLENGETYPE to dehydrated config
      command: incus exec {{ haproxy_container }} -- bash -c 'echo "CHALLENGETYPE=\"http-01\"" >> /etc/dehydrated/config'
      register: dehydrated_config_result2
      failed_when: false

    - name: Add WELLKNOWN to dehydrated config
      command: incus exec {{ haproxy_container }} -- bash -c 'echo "WELLKNOWN=\"/var/www/acme-challenge\"" >> /etc/dehydrated/config'
      register: dehydrated_config_result3
      failed_when: false

    - name: Add CONTACT_EMAIL to dehydrated config
      command: incus exec {{ haproxy_container }} -- bash -c 'echo "CONTACT_EMAIL=\"{{ acme_email }}\"" >> /etc/dehydrated/config'
      register: dehydrated_config_result4
      failed_when: false

    - name: Add HOOK to dehydrated config
      command: incus exec {{ haproxy_container }} -- bash -c 'echo "HOOK=\"/etc/dehydrated/hook.sh\"" >> /etc/dehydrated/config'
      register: dehydrated_config_result
      failed_when: false

    - name: Create dehydrated hook script
      command: |
        incus exec {{ haproxy_container }} -- bash -c 'cat > /etc/dehydrated/hook.sh << "EOF"
        #!/bin/bash
        # Dehydrated hook for HAProxy certificate management
        
        case "$1" in
            "deploy_cert")
                # Deploy certificate to HAProxy
                cat "$3" "$5" > /etc/haproxy/certs/${2}.pem
                systemctl reload haproxy
                ;;
            "clean_challenge")
                # Clean up challenge files
                rm -f /var/www/acme-challenge/*
                ;;
            "deploy_challenge")
                # Deploy challenge file
                cp "$2" "/var/www/acme-challenge/$3"
                ;;
            "unchanged_cert")
                # Certificate unchanged
                ;;
        esac
        EOF'
      register: hook_script_result
      failed_when: false

    - name: Make hook script executable
      command: |
        incus exec {{ haproxy_container }} -- chmod +x /etc/dehydrated/hook.sh
      register: hook_executable_result
      failed_when: false

    - name: Create HAProxy configuration
      command: |
        incus exec {{ haproxy_container }} -- tee /etc/haproxy/haproxy.cfg << 'EOF'
        global
            daemon
            user haproxy
            group haproxy
            log stdout local0
            chroot /var/lib/haproxy
            stats socket /run/haproxy/admin.sock mode 660 level admin
            stats timeout 30s
            tune.ssl.default-dh-param 2048

        defaults
            mode http
            log global
            option httplog
            option dontlognull
            option log-health-checks
            option forwardfor
            option httpchk GET /health
            timeout connect 5000
            timeout client 50000
            timeout server 50000
            errorfile 400 /etc/haproxy/errors/400.http
            errorfile 403 /etc/haproxy/errors/403.http
            errorfile 408 /etc/haproxy/errors/408.http
            errorfile 500 /etc/haproxy/errors/500.http
            errorfile 502 /etc/haproxy/errors/502.http
            errorfile 503 /etc/haproxy/errors/503.http
            errorfile 504 /etc/haproxy/errors/504.http

        # ACME challenge backend
        backend acme
            server acme 127.0.0.1:{{ webroot_port }} check

        # API backend
        backend be_api
            balance roundrobin
            option httpchk GET /api/health
            http-check expect status 200
            server api1 10.10.0.101:8080 check inter 2000 rise 2 fall 3

        # WebSocket backend
        backend be_ws
            mode tcp
            balance roundrobin
            server ws1 10.10.0.102:8081 check inter 2000 rise 2 fall 3

        # Stream backend
        backend be_stream
            balance roundrobin
            option httpchk GET /stream/health
            http-check expect status 200
            server stream1 10.10.0.103:8082 check inter 2000 rise 2 fall 3

        # Web frontend backend
        backend be_web
            balance roundrobin
            option httpchk GET /
            http-check expect status 200
            server web1 10.10.0.104:3000 check inter 2000 rise 2 fall 3

        # HTTP frontend (redirect to HTTPS)
        frontend http_frontend
            bind *:80
            acl acme_challenge path_beg /.well-known/acme-challenge/
            use_backend acme if acme_challenge
            redirect scheme https code 301 if !acme_challenge

        # HTTPS frontend
        frontend https_frontend
            bind *:443 ssl crt /etc/haproxy/certs/{{ domain }}.pem alpn h2,http/1.1
            
            # Security headers
            http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
            http-response set-header X-Content-Type-Options "nosniff"
            http-response set-header X-Frame-Options "DENY"
            http-response set-header X-XSS-Protection "1; mode=block"
            http-response set-header Referrer-Policy "no-referrer"
            http-response set-header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss:;"
            
            # Routing rules
            acl is_api path_beg /api
            acl is_ws path_beg /ws
            acl is_stream path_beg /stream
            
            use_backend be_api if is_api
            use_backend be_ws if is_ws
            use_backend be_stream if is_stream
            default_backend be_web

        # Statistics
        listen stats
            bind *:8404
            stats enable
            stats uri /stats
            stats refresh 30s
            stats admin if TRUE
        EOF
      register: haproxy_config_result
      failed_when: false

    - name: Create HAProxy certificates directory
      command: |
        incus exec {{ haproxy_container }} -- mkdir -p /etc/haproxy/certs
      register: certs_dir_result
      failed_when: false

    - name: Generate self-signed certificate (temporary)
      command: |
        incus exec {{ haproxy_container }} -- openssl req -x509 -newkey rsa:4096 -keyout /etc/haproxy/certs/{{ domain }}.pem -out /etc/haproxy/certs/{{ domain }}.pem -days 365 -nodes -subj "/C=FR/ST=France/L=Paris/O=Veza/OU=IT/CN={{ domain }}"
      register: self_signed_result
      failed_when: false

    - name: Start HAProxy service
      command: |
        incus exec {{ haproxy_container }} -- systemctl enable haproxy
        incus exec {{ haproxy_container }} -- systemctl start haproxy
      register: haproxy_start_result
      failed_when: false

    - name: Check HAProxy status
      command: |
        incus exec {{ haproxy_container }} -- systemctl status haproxy
      register: haproxy_status
      failed_when: false

    - name: Display HAProxy status
      debug:
        var: haproxy_status.stdout_lines

    - name: Request Let's Encrypt certificate
      command: |
        incus exec {{ haproxy_container }} -- dehydrated -c -d {{ domain }}
      register: acme_cert_result
      failed_when: false

    - name: Display ACME certificate result
      debug:
        var: acme_cert_result.stdout_lines

    - name: Setup certificate renewal cron
      command: |
        incus exec {{ haproxy_container }} -- tee /etc/cron.d/dehydrated << 'EOF'
        0 12 * * * root /usr/bin/dehydrated -c
        EOF
      register: cron_result
      failed_when: false

    - name: Test HAProxy configuration
      command: |
        incus exec {{ haproxy_container }} -- haproxy -c -f /etc/haproxy/haproxy.cfg
      register: haproxy_test_result
      failed_when: false

    - name: Display HAProxy test result
      debug:
        var: haproxy_test_result.stdout_lines

  post_tasks:
    - name: Show HAProxy statistics
      command: |
        incus exec {{ haproxy_container }} -- curl -s http://localhost:8404/stats
      register: haproxy_stats
      failed_when: false

    - name: Display HAProxy statistics
      debug:
        var: haproxy_stats.stdout_lines
