---
- name: Group hosts by platform
  hosts: all
  tags:
    - always
  pre_tasks:
    - include_tasks: tasks/meta/runtime-group-determination.yaml


- name: Bootstrap remote ansible environment
  hosts: linux
  gather_facts: false
  tags:
    - always
  tasks:
    - include_tasks: tasks/meta/bootstrap-remote-env.yaml


- name: Update system
  hosts: linux
  tags:
    - packages
  vars_files:
    - vars/packages.yaml
  tasks:
    - name: Update system packages via DNF
      when: ansible_distribution == "Rocky" or ansible_distribution == "Fedora"
      become: true
      ansible.builtin.dnf:
        name: "*"
        state: latest

    - name: Install global bash config
      become: true
      ansible.builtin.copy:
        src: global.sh
        dest: /etc/profile.d/ZZ-skylab-global.sh
        owner: root
        group: "{{ ansible_user }}"
        mode: 0644

    - name: Install universal packages on Rocky
      when: ansible_distribution == "Rocky"
      become: true
      ansible.builtin.dnf:
        name: "{{ skylab_packages_global + skylab_packages_rocky }}"
        state: present
        update_cache: true

    - name: Install universal packages on Fedora
      when: ansible_distribution == "Fedora"
      become: true
      ansible.builtin.dnf:
        name: "{{ skylab_packages_global + skylab_packages_fedora }}"
        state: present
        update_cache: true


- name: Update unix accounts
  hosts: linux
  tags:
    - accounts
    - access
  vars_files:
    - vars/access.yaml
    - vars/sshkeys.yaml
  tasks:
    - name: Create management groups
      become: true
      ansible.builtin.group:
        name: "{{ item.name }}"
        gid: "{{ item.gid }}"
        state: present
      loop:
        - "{{ skylab_group }}"
        - "{{ skylab_group_admin }}"
        - "{{ skylab_group_automation }}"
      loop_control:
        label: "{{ item.name }},{{ item.gid }}"

    - name: Determine existing skylab users
      changed_when: false
      ansible.builtin.shell:
        cmd: 'grep "{{ skylab_group.name }}:" /etc/group | cut --delimiter : --fields 4 | tr "," "\n"'
      register: _existing_skylab_accounts

    - name: Determine deleted skylab users
      vars:
        _deleted_accounts: []
      when: item not in (skylab_accounts | items2dict(key_name='name', value_name='uid'))
      ansible.builtin.set_fact:
        _deleted_accounts: "{{ _deleted_accounts + [item] }}"
      loop: "{{ _existing_skylab_accounts.stdout_lines }}"

    - name: Delete accounts
      when: _deleted_accounts | default(false)
      block:
        - name: Delete removed user accounts
          become: true
          ansible.builtin.user:
            name: "{{ item }}"
            state: absent
          loop: "{{ _deleted_accounts }}"

        - name: Delete removed user groups
          become: true
          ansible.builtin.group:
            name: "{{ item }}"
            state: absent
          loop: "{{ _deleted_accounts }}"

        - name: Delete removed user home directories
          become: true
          ansible.builtin.file:
            path: "/home/{{ item }}"
            state: absent
          loop: "{{ _deleted_accounts }}"

    - name: Determine active users
      when: item.targets | default([]) | intersect(skylab_targets)
      vars:
        _active_accounts: []
      ansible.builtin.set_fact:
        _active_accounts: "{{ _active_accounts + [item] }}"
      loop: "{{ skylab_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Create account groups
      become: true
      ansible.builtin.group:
        name: "{{ item.name }}"
        gid: "{{ item.uid }}"
        state: present
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Determine account groups
      ansible.builtin.set_fact:
        _determined_member_groups: "{{ _determined_member_groups | default({}) | combine({item.name: [
          skylab_group.name,
          'wheel' if (item.admin | default(false) and ansible_os_family == 'RedHat') else '',
          'sudo' if (item.admin | default(false) and ansible_os_family == 'Debian') else '',
          skylab_group_admin.name if item.admin | default(false) else '',
          skylab_group_automation.name if item.service | default(false) else '',
        ]}) }}"
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Create accounts
      become: true
      ansible.builtin.user:
        name: "{{ item.name }}"
        state: present
        uid: "{{ item.uid }}"
        group: "{{ item.name }}"
        groups: "{{ _determined_member_groups[item.name] }}"
        comment: "{{ item.fullname | default('') }}"
        system: "{{ item.service | default(false) }}"
        generate_ssh_key: true
        ssh_key_bits: 4096
        ssh_key_passphrase: "{{ item.password }}"
        ssh_key_comment: "{{ item.name }}@{{ inventory_hostname }}"
        ssh_key_type: ed25519
        password: "{{ item.password }}"
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Ensure proper ownership of user home directories
      become: true
      ansible.builtin.file:
        path: /home/{{ item.name }}
        state: directory
        group: "{{ item.name }}"
        owner: "{{ item.name }}"
        mode: 0700
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Create SSH directory
      become: true
      ansible.builtin.file:
        path: /home/{{ item.name }}/.ssh
        owner: "{{ item.name }}"
        group: "{{ item.name }}"
        state: directory
        mode: 0700
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Update authorized keys
      become: true
      ansible.builtin.authorized_key:
        user: "{{ item.name }}"
        key: "{{ skylab_ssh_keys[item.name] | join('\n') }}"
        state: present
        exclusive: true
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Enforce ownership of authorized keys
      become: true
      ansible.builtin.file:
        path: /home/{{ item.name }}/.ssh/authorized_keys
        state: file
        owner: "{{ item.name }}"
        group: "{{ item.name }}"
        mode: 0400
      loop: "{{ _active_accounts }}"
      loop_control:
        label: "{{ item.uid }},{{ item.name }}"

    - name: Enforce root password
      become: true
      ansible.builtin.user:
        name: root
        password: "{{ skylab_root_password }}"
        state: present
