---
# showcase/webapp-web.yml — nginx + PHP-FPM setup for the webapp blueprint
#
# Discovers the db sibling via Incus API, reads db credentials from the
# db instance, deploys a PHP app that shows DB connectivity, and adds
# an ACL rule to the db's ACL allowing this web instance on port 3306.

# --- Step 1: Find the database instance from the deployment manifest ---

- name: Find database instances from deployment
  ansible.builtin.set_fact:
    db_instances: >-
      {{ ffsdn_instances | selectattr('component_name', 'equalto', 'db') | list }}

- name: Validate database found
  ansible.builtin.assert:
    that:
      - db_instances | length > 0
    fail_msg: >-
      No db component found in ffsdn_instances. The blueprint must
      include a db component for the web tier to connect to.

- name: Select primary database instance
  ansible.builtin.set_fact:
    db_instance:
      name: "{{ db_instances[0].name }}"
      ip: "{{ db_instances[0].ip }}"

- name: Display database target
  ansible.builtin.debug:
    msg: "Using database: {{ db_instance.name }} at {{ db_instance.ip }}"

# --- Step 2: Wait for and read database credentials ---

- name: Wait for db credentials to be available (up to 5 minutes)
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/instances/{{ db_instance.name }}/files?path=/etc/webapp-db-creds"
    method: GET
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    return_content: true
    status_code: [200, 404]
  register: db_creds_response
  until: db_creds_response.status == 200
  retries: 30
  delay: 10

- name: Parse database credentials
  ansible.builtin.set_fact:
    db_host: "{{ db_creds_response.content | regex_search('DB_HOST=(\\S+)', '\\1') | first }}"
    db_port: "{{ db_creds_response.content | regex_search('DB_PORT=(\\S+)', '\\1') | first }}"
    db_name: "{{ db_creds_response.content | regex_search('DB_NAME=(\\S+)', '\\1') | first }}"
    db_user: "{{ db_creds_response.content | regex_search('DB_USER=(\\S+)', '\\1') | first }}"
    db_pass: "{{ db_creds_response.content | regex_search('DB_PASS=(\\S+)', '\\1') | first }}"

- name: Display parsed credentials
  ansible.builtin.debug:
    msg: "DB connection: {{ db_user }}@{{ db_host }}:{{ db_port }}/{{ db_name }}"

# --- Step 3: Install nginx + PHP-FPM and deploy app ---

- name: Generate web setup script
  ansible.builtin.set_fact:
    web_setup_script: |
      #!/bin/bash
      set -e

      echo "=== Web tier setup starting ==="
      export DEBIAN_FRONTEND=noninteractive

      # Install nginx + PHP-FPM + MySQL extension
      apt-get install -y -qq nginx php-fpm php-mysql 2>&1 | tail -3
      echo "[OK] nginx + PHP-FPM + php-mysql installed"

      # Find PHP-FPM socket path (varies by PHP version)
      PHP_SOCK=$(find /run/php/ -name 'php*-fpm.sock' 2>/dev/null | head -1)
      if [ -z "$PHP_SOCK" ]; then
        PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
        PHP_SOCK="/run/php/php${PHP_VERSION}-fpm.sock"
      fi
      echo "[OK] PHP-FPM socket: $PHP_SOCK"

      # Configure nginx with PHP
      cat > /etc/nginx/sites-available/default << NGINX
      server {
          listen 80 default_server;
          root /var/www/html;
          index index.php index.html;
          server_name _;

          location / {
              try_files \$uri \$uri/ =404;
          }

          location ~ \\.php\$ {
              include snippets/fastcgi-php.conf;
              fastcgi_pass unix:${PHP_SOCK};
          }
      }
      NGINX
      echo "[OK] nginx configured"

      # Deploy PHP application
      cat > /var/www/html/index.php << 'PHPAPP'
      <?php
      $config = [];
      foreach (file('/etc/webapp-web-config') as $line) {
          $line = trim($line);
          if ($line && strpos($line, '=') !== false) {
              list($key, $value) = explode('=', $line, 2);
              $config[trim($key)] = trim($value);
          }
      }

      $db_host = $config['DB_HOST'] ?? 'unknown';
      $db_port = $config['DB_PORT'] ?? '3306';
      $db_name = $config['DB_NAME'] ?? 'webapp';
      $db_user = $config['DB_USER'] ?? 'webapp';
      $db_pass = $config['DB_PASS'] ?? '';

      $instance = gethostname();
      $ip = $config['INSTANCE_IP'] ?? 'unknown';
      $db_status = 'disconnected';
      $visit_count = 0;
      $error = '';

      try {
          $pdo = new PDO(
              "mysql:host={$db_host};port={$db_port};dbname={$db_name}",
              $db_user, $db_pass,
              [PDO::ATTR_TIMEOUT => 5]
          );
          $db_status = 'connected';

          // Record this visit
          $stmt = $pdo->prepare('INSERT INTO visits (visitor_ip, visitor_host) VALUES (?, ?)');
          $stmt->execute([$_SERVER['REMOTE_ADDR'] ?? 'direct', $instance]);

          // Count total visits
          $visit_count = $pdo->query('SELECT COUNT(*) FROM visits')->fetchColumn();
      } catch (PDOException $e) {
          $error = $e->getMessage();
          $db_status = 'error';
      }
      ?>
      <!DOCTYPE html>
      <html>
      <head>
          <title>Webapp Showcase — <?= htmlspecialchars($instance) ?></title>
          <style>
              body { font-family: -apple-system, sans-serif; max-width: 700px; margin: 40px auto; padding: 0 20px; background: #f5f5f5; }
              .card { background: white; border-radius: 8px; padding: 24px; margin: 16px 0; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
              h1 { color: #1a1a2e; }
              .status { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 14px; font-weight: 600; }
              .connected { background: #d4edda; color: #155724; }
              .error, .disconnected { background: #f8d7da; color: #721c24; }
              table { width: 100%; border-collapse: collapse; }
              td { padding: 8px; border-bottom: 1px solid #eee; }
              td:first-child { font-weight: 600; width: 40%; color: #555; }
              .footer { text-align: center; color: #888; font-size: 13px; margin-top: 24px; }
          </style>
      </head>
      <body>
          <h1>Webapp Showcase</h1>
          <div class="card">
              <h2>Instance Info</h2>
              <table>
                  <tr><td>Instance</td><td><?= htmlspecialchars($instance) ?></td></tr>
                  <tr><td>IP Address</td><td><?= htmlspecialchars($ip) ?></td></tr>
                  <tr><td>Component</td><td>web</td></tr>
              </table>
          </div>
          <div class="card">
              <h2>Database Connection <span class="status <?= $db_status ?>"><?= $db_status ?></span></h2>
              <table>
                  <tr><td>Host</td><td><?= htmlspecialchars($db_host) ?>:<?= htmlspecialchars($db_port) ?></td></tr>
                  <tr><td>Database</td><td><?= htmlspecialchars($db_name) ?></td></tr>
                  <tr><td>Total Visits</td><td><?= $visit_count ?></td></tr>
                  <?php if ($error): ?>
                  <tr><td>Error</td><td style="color:#721c24"><?= htmlspecialchars($error) ?></td></tr>
                  <?php endif; ?>
              </table>
          </div>
          <div class="footer">
              Deployed by AWX via Aether &mdash; <?= date('Y-m-d H:i:s T') ?>
          </div>
      </body>
      </html>
      PHPAPP
      echo "[OK] PHP application deployed"

      # Remove default HTML page
      rm -f /var/www/html/index.html /var/www/html/index.nginx-debian.html

      # Detect PHP-FPM service name (e.g., php8.2-fpm)
      PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
      PHP_FPM_SVC="php${PHP_VERSION}-fpm"

      # Restart services
      systemctl enable nginx "$PHP_FPM_SVC"
      systemctl restart "$PHP_FPM_SVC"
      systemctl restart nginx
      echo "[OK] Services started (PHP-FPM: $PHP_FPM_SVC)"

      echo "=== Web tier setup complete ==="

- name: Generate web config file content
  ansible.builtin.set_fact:
    web_config_content: |
      DB_HOST={{ db_host }}
      DB_PORT={{ db_port }}
      DB_NAME={{ db_name }}
      DB_USER={{ db_user }}
      DB_PASS={{ db_pass }}
      INSTANCE_IP={{ ffsdn_instance_ip | default('unknown') }}

- name: Push web config to instance
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/etc/webapp-web-config"
    method: POST
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    body: "{{ web_config_content }}"
    headers:
      Content-Type: "application/octet-stream"
      X-Incus-type: "file"
      X-Incus-mode: "0644"
    status_code: [200]

- name: Push web setup script
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/files?path=/tmp/webapp-web-setup.sh"
    method: POST
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    body: "{{ web_setup_script }}"
    headers:
      Content-Type: "application/octet-stream"
      X-Incus-type: "file"
      X-Incus-mode: "0755"
    status_code: [200]

- name: Execute web setup script
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
    method: POST
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    body_format: json
    body:
      command: ["/bin/bash", "/tmp/webapp-web-setup.sh"]
      record-output: true
      interactive: false
      wait-for-websocket: false
      environment:
        DEBIAN_FRONTEND: "noninteractive"
    status_code: [202]
    return_content: true
  register: web_setup_exec

- name: Wait for web setup to complete (up to 5 minutes)
  ansible.builtin.uri:
    url: "{{ incus_api }}{{ web_setup_exec.json.operation }}/wait?timeout=300"
    method: GET
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    return_content: true
    timeout: 320
  register: web_setup_wait
  failed_when: web_setup_wait.json.metadata.status != "Success"

- name: Verify web setup exit code
  ansible.builtin.assert:
    that:
      - web_setup_wait.json.metadata.metadata.return | int == 0
    fail_msg: "Web setup exited with code {{ web_setup_wait.json.metadata.metadata.return }}"
    success_msg: "Web tier setup completed successfully"

# --- Step 4: Add ACL rule to allow this web instance to reach the DB ---

- name: Build db ACL name
  ansible.builtin.set_fact:
    db_acl_name: "52-{{ db_instance.name }}-aether-acl"

- name: Read current db ACL
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/network-acls/{{ db_acl_name }}"
    method: GET
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    return_content: true
    status_code: [200, 404]
  register: db_acl_response

- name: Build updated ingress rules (append web access)
  ansible.builtin.set_fact:
    updated_ingress: >-
      {{ (db_acl_response.json.metadata.ingress | default([])) + [{
        'action': 'allow',
        'state': 'enabled',
        'source': (ffsdn_instance_ip | default('')) + '/32',
        'destination': db_instance.ip + '/32',
        'protocol': 'tcp',
        'destination_port': '3306',
        'description': 'Allow ' + ffsdn_instance_name + ' to db on 3306'
      }] }}
  when: db_acl_response.status == 200

- name: Update db ACL with web access rule
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/network-acls/{{ db_acl_name }}"
    method: PATCH
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    body_format: json
    body:
      ingress: "{{ updated_ingress }}"
    status_code: [200]
  when:
    - db_acl_response.status == 200
    - updated_ingress is defined

- name: Report ACL update
  ansible.builtin.debug:
    msg: >-
      {% if db_acl_response.status == 200 %}ACL '{{ db_acl_name }}' updated:
      {{ ffsdn_instance_name }} ({{ ffsdn_instance_ip | default('?') }}) → {{ db_instance.name }}:3306
      {% else %}ACL '{{ db_acl_name }}' not found — skipping ACL rule (Aether may not have created it yet){% endif %}

- name: Clean up setup script
  ansible.builtin.uri:
    url: "{{ incus_api }}/1.0/instances/{{ ffsdn_instance_name }}/exec"
    method: POST
    client_cert: "{{ incus_cert }}"
    client_key: "{{ incus_key }}"
    validate_certs: false
    body_format: json
    body:
      command: ["/bin/rm", "-f", "/tmp/webapp-web-setup.sh"]
      record-output: false
      interactive: false
      wait-for-websocket: false
    status_code: [202]
  ignore_errors: true
