---
# Configure HAProxy inside container with Let's Encrypt ACME HTTP-01
# Handles SSL termination and routing for Veza V5 Ultra

- name: Configure HAProxy in container with ACME
  hosts: edge
  become: true
  gather_facts: true

  vars:
    haproxy_container: veza-haproxy
    acme_webroot_port: 8888
    haproxy_certs_dir: /etc/haproxy/certs
    acme_webroot_dir: /var/www/acme-challenge

  tasks:
    - name: Install HAProxy and ACME tools in container
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          apt update && apt install -y \
            haproxy \
            curl \
            wget \
            socat \
            cron \
            nginx-light \
            openssl
        "
      register: haproxy_install_result
      failed_when: false

    - name: Display HAProxy installation result
      debug:
        var: haproxy_install_result.stdout_lines
      when: haproxy_install_result.stdout_lines is defined

    - name: Create ACME webroot directory
      command: |
        incus exec {{ haproxy_container }} -- mkdir -p {{ acme_webroot_dir }}
      register: webroot_create_result
      failed_when: false

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

    - name: Install dehydrated for ACME
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          cd /opt && \
          git clone https://github.com/dehydrated-io/dehydrated.git && \
          chmod +x dehydrated/dehydrated
        "
      register: dehydrated_install_result
      failed_when: false

    - name: Create dehydrated configuration
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          cat > /opt/dehydrated/config << 'EOF'
        WELLKNOWN={{ acme_webroot_dir }}
        DOMAINS_TXT=/opt/dehydrated/domains.txt
        HOOK=/opt/dehydrated/hook.sh
        CHALLENGETYPE=http-01
        EOF
        "
      register: dehydrated_config_result
      failed_when: false

    - name: Create domains file for ACME
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          echo '{{ domain }}' > /opt/dehydrated/domains.txt
        "
      register: domains_file_result
      failed_when: false

    - name: Create ACME hook script
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          cat > /opt/dehydrated/hook.sh << 'EOF'
        #!/bin/bash
        case \"\$1\" in
            deploy_challenge)
                # Start nginx for ACME challenge
                nginx -c /etc/nginx/nginx.conf -g 'daemon on;'
                ;;
            clean_challenge)
                # Stop nginx after challenge
                nginx -s quit
                ;;
            deploy_cert)
                # Combine cert and key for HAProxy
                cat \$3 \$5 > {{ haproxy_certs_dir }}/{{ domain }}.pem
                # Reload HAProxy
                systemctl reload haproxy
                ;;
        esac
        EOF
          chmod +x /opt/dehydrated/hook.sh
        "
      register: hook_script_result
      failed_when: false

    - name: Create nginx config for ACME webroot
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          cat > /etc/nginx/nginx.conf << 'EOF'
        events { worker_connections 1024; }
        http {
            server {
                listen {{ acme_webroot_port }};
                location /.well-known/acme-challenge/ {
                    root {{ acme_webroot_dir }};
                }
            }
        }
        EOF
        "
      register: nginx_config_result
      failed_when: false

    - name: Create HAProxy configuration
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          cat > /etc/haproxy/haproxy.cfg << 'EOF'
        global
            log stdout local0
            chroot /var/lib/haproxy
            stats socket /run/haproxy/admin.sock mode 660 level admin
            stats timeout 30s
            user haproxy
            group haproxy
            daemon
            maxconn 20000
            ssl-default-bind-options no-sslv3 no-tls-tickets
            ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
            ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256

        defaults
            log global
            mode http
            option httplog
            option dontlognull
            option forwardfor
            timeout connect 5s
            timeout client 50s
            timeout server 50s
            timeout http-request 5s
            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

        frontend http_frontend
            bind *:80
            acl acme_challenge path_beg /.well-known/acme-challenge/
            use_backend acme_backend if acme_challenge
            redirect scheme https code 301 if !acme_challenge

        frontend https_frontend
            bind *:443 ssl crt {{ haproxy_certs_dir }}/{{ domain }}.pem alpn h2,http/1.1
            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 Referrer-Policy no-referrer
            http-response set-header Content-Security-Policy \"default-src 'self'; connect-src 'self' wss://{{ domain }} https://{{ domain }}; img-src 'self' data:; script-src 'self'; style-src 'self' 'unsafe-inline'\"
            
            acl is_api path_beg /api
            acl is_ws path_beg /ws
            acl is_stream path_beg /stream
            
            use_backend api_backend if is_api
            use_backend ws_backend if is_ws
            use_backend stream_backend if is_stream
            default_backend web_backend

        backend acme_backend
            server acme_server 127.0.0.1:{{ acme_webroot_port }}

        backend api_backend
            server api_server veza-backend:{{ veza_backend_port }} check

        backend ws_backend
            mode tcp
            server ws_server veza-chat:{{ veza_chat_port }} check

        backend stream_backend
            server stream_server veza-stream:{{ veza_stream_port }} check

        backend web_backend
            server web_server veza-web:{{ veza_web_port }} check
        EOF
        "
      register: haproxy_config_result
      failed_when: false

    - name: Create self-signed certificate for initial setup
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          openssl req -x509 -newkey rsa:2048 -keyout {{ haproxy_certs_dir }}/{{ domain }}.key -out {{ haproxy_certs_dir }}/{{ domain }}.crt -days 365 -nodes -subj '/CN={{ domain }}' && \
          cat {{ haproxy_certs_dir }}/{{ domain }}.crt {{ haproxy_certs_dir }}/{{ domain }}.key > {{ haproxy_certs_dir }}/{{ domain }}.pem
        "
      register: selfsigned_cert_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: Display HAProxy start result
      debug:
        var: haproxy_start_result.stdout_lines
      when: haproxy_start_result.stdout_lines is defined

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

    - name: Display HAProxy status
      debug:
        var: haproxy_status_result.stdout_lines
      when: haproxy_status_result.stdout_lines is defined

    - name: Create ACME renewal cron job
      command: |
        incus exec {{ haproxy_container }} -- bash -c "
          echo '0 2 * * * /opt/dehydrated/dehydrated -c' | crontab -
        "
      register: cron_setup_result
      failed_when: false

    - name: Display cron setup result
      debug:
        var: cron_setup_result.stdout_lines
      when: cron_setup_result.stdout_lines is defined

  post_tasks:
    - 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
      when: haproxy_test_result.stdout_lines is defined

    - name: Show final HAProxy status
      command: |
        incus exec {{ haproxy_container }} -- netstat -tlnp | grep haproxy
      register: final_haproxy_status
      failed_when: false

    - name: Display final HAProxy status
      debug:
        var: final_haproxy_status.stdout_lines
      when: final_haproxy_status.stdout_lines is defined
