---
# Portainer Stack Management via API
# Manages GitOps stacks across all Portainer endpoints
# Run with: ansible-playbook -i hosts.ini playbooks/portainer_stack_management.yml

- name: Portainer Stack Management
  hosts: localhost
  gather_facts: no
  vars:
    portainer_url: "https://192.168.0.200:9443"
    portainer_username: "admin"
    # portainer_password: "{{ vault_portainer_password }}"  # Use ansible-vault
    git_repo_url: "https://git.vish.gg/Vish/homelab.git"

    # Portainer endpoints mapping
    endpoints:
      atlantis:
        id: 1
        name: "Atlantis"
        stacks_path: "Atlantis"
      calypso:
        id: 2
        name: "Calypso"
        stacks_path: "Calypso"
      concord_nuc:
        id: 3
        name: "Concord NUC"
        stacks_path: "concord_nuc"
      homelab_vm:
        id: 4
        name: "Homelab VM"
        stacks_path: "homelab_vm"
      rpi5:
        id: 5
        name: "RPi 5"
        stacks_path: "raspberry-pi-5-vish"

  tasks:
    - name: Authenticate with Portainer
      uri:
        url: "{{ portainer_url }}/api/auth"
        method: POST
        body_format: json
        body:
          Username: "{{ portainer_username }}"
          Password: "{{ portainer_password | default('admin') }}"
        validate_certs: no
      register: auth_response
      no_log: true

    - name: Set authentication token
      set_fact:
        portainer_token: "{{ auth_response.json.jwt }}"

    - name: Get all endpoints
      uri:
        url: "{{ portainer_url }}/api/endpoints"
        method: GET
        headers:
          Authorization: "Bearer {{ portainer_token }}"
        validate_certs: no
      register: endpoints_response

    - name: Display available endpoints
      debug:
        msg: |
          Available Portainer Endpoints:
          {% for endpoint in endpoints_response.json %}
          - ID: {{ endpoint.Id }}, Name: {{ endpoint.Name }}, Status: {{ endpoint.Status }}
          {% endfor %}

    - name: Get stacks for each endpoint
      uri:
        url: "{{ portainer_url }}/api/stacks"
        method: GET
        headers:
          Authorization: "Bearer {{ portainer_token }}"
        validate_certs: no
      register: stacks_response

    - name: Analyze GitOps stacks
      set_fact:
        gitops_stacks: "{{ stacks_response.json | selectattr('GitConfig', 'defined') | list }}"
        non_gitops_stacks: "{{ stacks_response.json | rejectattr('GitConfig', 'defined') | list }}"

    - name: Display GitOps status
      debug:
        msg: |
          GitOps Stack Analysis:
          - Total Stacks: {{ stacks_response.json | length }}
          - GitOps Managed: {{ gitops_stacks | length }}
          - Non-GitOps: {{ non_gitops_stacks | length }}

          GitOps Stacks:
          {% for stack in gitops_stacks %}
          - {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
          {% endfor %}

          Non-GitOps Stacks:
          {% for stack in non_gitops_stacks %}
          - {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
          {% endfor %}

    - name: Check stack health
      uri:
        url: "{{ portainer_url }}/api/stacks/{{ item.Id }}/file"
        method: GET
        headers:
          Authorization: "Bearer {{ portainer_token }}"
        validate_certs: no
      register: stack_files
      loop: "{{ gitops_stacks }}"
      failed_when: false

    - name: Trigger GitOps sync for all stacks
      uri:
        url: "{{ portainer_url }}/api/stacks/{{ item.Id }}/git/redeploy"
        method: PUT
        headers:
          Authorization: "Bearer {{ portainer_token }}"
        body_format: json
        body:
          RepositoryReferenceName: "refs/heads/main"
          PullImage: true
        validate_certs: no
      register: sync_results
      loop: "{{ gitops_stacks }}"
      when: sync_stacks | default(false) | bool
      failed_when: false

    - name: Display sync results
      debug:
        msg: |
          GitOps Sync Results:
          {% for result in sync_results.results %}
          {% if result.skipped is not defined %}
          - Stack: {{ gitops_stacks[loop.index0].Name }} - Status: {{ result.status | default('Failed') }}
          {% endif %}
          {% endfor %}
      when: sync_stacks | default(false) | bool

    - name: Generate stack health report
      copy:
        content: |
          # Portainer Stack Health Report
          Generated: {{ ansible_date_time.iso8601 }}

          ## Summary
          - Total Stacks: {{ stacks_response.json | length }}
          - GitOps Managed: {{ gitops_stacks | length }}
          - Non-GitOps: {{ non_gitops_stacks | length }}

          ## GitOps Stacks
          {% for stack in gitops_stacks %}
          ### {{ stack.Name }}
          - Endpoint: {{ stack.EndpointId }}
          - Status: {{ stack.Status }}
          - Git Repository: {{ stack.GitConfig.URL if stack.GitConfig is defined else 'N/A' }}
          - Git Reference: {{ stack.GitConfig.ReferenceName if stack.GitConfig is defined else 'N/A' }}
          - Last Update: {{ stack.UpdatedAt }}

          {% endfor %}

          ## Non-GitOps Stacks (Manual Management Required)
          {% for stack in non_gitops_stacks %}
          - {{ stack.Name }} (Endpoint: {{ stack.EndpointId }})
          {% endfor %}
        dest: "/tmp/portainer_stack_report_{{ ansible_date_time.epoch }}.md"
      delegate_to: localhost

    - name: Display report location
      debug:
        msg: "Stack health report saved to: /tmp/portainer_stack_report_{{ ansible_date_time.epoch }}.md"
