---
- name: Configure elogind to suspend on lid close
  tags: [packages]
  ansible.builtin.lineinfile:
    path: /etc/elogind/logind.conf
    regexp: '^#?HandleLidSwitch='
    line: 'HandleLidSwitch=suspend'
    state: present
  when: "'void' in group_names"

- name: Ensure common config directories exist
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0755"
  loop:
    - "{{ user_home }}/.config"
    - "{{ user_home }}/.config/autostart"
    - "{{ user_home }}/.bashrc.d"
    - "{{ user_home }}/.tmux"
    - "{{ user_home }}/.tmux/bin"
    - "{{ user_home }}/.tmux/plugins"
    - "{{ user_home }}/.ssh"

- name: Ensure user local bin directory exists
  tags: [dotfiles, dotfiles:desktop, dotfiles:host]
  ansible.builtin.file:
    path: "{{ user_home }}/.local/bin"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0755"

- name: Enable gnome-keyring PAM auth hook
  tags: [packages, gnome]
  ansible.builtin.lineinfile:
    path: /etc/pam.d/login
    insertafter: '^auth\s+include\s+system-local-login$'
    line: "auth       optional  pam_gnome_keyring.so"
    state: present
  when: "'void' in group_names"

- name: Enable gnome-keyring PAM session hook
  tags: [packages, gnome]
  ansible.builtin.lineinfile:
    path: /etc/pam.d/login
    insertafter: '^session\s+include\s+system-local-login$'
    line: "session    optional  pam_gnome_keyring.so auto_start"
    state: present
  when: "'void' in group_names"

- name: Enable gnome-keyring PAM password hook
  tags: [packages, gnome]
  ansible.builtin.lineinfile:
    path: /etc/pam.d/login
    insertafter: '^password\s+include\s+system-local-login$'
    line: "password   optional  pam_gnome_keyring.so use_authtok"
    state: present
  when: "'void' in group_names"

- name: Check whether SSH host ed25519 key exists
  tags: [services]
  ansible.builtin.stat:
    path: /etc/ssh/ssh_host_ed25519_key
  register: desktop_ssh_host_ed25519_key
  when:
    - (host_sshd_settings | default({})) | length > 0
      or (host_sshd_allow_users | default([])) | length > 0

- name: Generate missing SSH host keys on desktop host
  tags: [services]
  ansible.builtin.command: ssh-keygen -A
  changed_when: true
  when:
    - (host_sshd_settings | default({})) | length > 0
      or (host_sshd_allow_users | default([])) | length > 0
    - not desktop_ssh_host_ed25519_key.stat.exists

- name: Require authorized SSH keys before disabling password authentication on desktop host
  tags: [services]
  ansible.builtin.assert:
    that:
      - (host_authorized_ssh_keys | default([])) | length > 0
    fail_msg: >-
      SSH password authentication is disabled for this host, but no authorized SSH
      keys are defined. Set vault_ikaros_authorized_ssh_keys in secrets/vault.yml
      or secrets/vault.local.yml before applying this configuration.
  when:
    - "'sshd' in (host_enabled_services | default([]))"
    - (host_sshd_settings | default({})).PasswordAuthentication | default('yes') == 'no'

- name: Ensure desktop user SSH directory exists
  tags: [services, dotfiles]
  ansible.builtin.file:
    path: "{{ user_home }}/.ssh"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0700"
  when: (host_authorized_ssh_keys | default([])) | length > 0

- name: Ensure desktop user authorized_keys file exists
  tags: [services, dotfiles]
  ansible.builtin.file:
    path: "{{ user_home }}/.ssh/authorized_keys"
    state: touch
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0600"
  when: (host_authorized_ssh_keys | default([])) | length > 0

- name: Manage desktop user authorized SSH keys exclusively
  tags: [services, dotfiles]
  ansible.posix.authorized_key:
    user: "{{ username }}"
    key: "{{ host_authorized_ssh_keys | join('\n') }}"
    state: present
    exclusive: true
    manage_dir: false
  when: (host_authorized_ssh_keys | default([])) | length > 0

- name: Apply SSH daemon settings on desktop host
  tags: [services]
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^\s*{{ item.key }}\s+'
    line: "{{ item.key }} {{ item.value }}"
    state: present
    validate: "sshd -t -f %s"
  notify: Reload SSH service
  loop: "{{ host_sshd_settings | default({}) | dict2items }}"
  loop_control:
    label: "{{ item.key }}"
  when: (host_sshd_settings | default({})) | length > 0

- name: Restrict SSH login to allowed desktop users
  tags: [services]
  ansible.builtin.lineinfile:
    path: /etc/ssh/sshd_config
    regexp: '^\s*AllowUsers\s+'
    line: "AllowUsers {{ host_sshd_allow_users | join(' ') }}"
    state: present
    validate: "sshd -t -f %s"
  notify: Reload SSH service
  when: (host_sshd_allow_users | default([])) | length > 0

- name: Define effective desktop UFW rules
  tags: [services, packages]
  ansible.builtin.set_fact:
    desktop_ufw_rules_effective: "{{ host_ufw_rules | default([]) }}"

- name: Apply host UFW rules on desktop
  tags: [services, packages]
  community.general.ufw:
    rule: "{{ item.rule }}"
    name: "{{ item.name | default(omit) }}"
    port: "{{ item.port | default(omit) }}"
    proto: "{{ item.proto | default(omit) }}"
    from_ip: "{{ item.src | default(omit) }}"
    to_ip: "{{ item.dest | default(omit) }}"
    from_port: "{{ item.from_port | default(omit) }}"
    direction: "{{ item.direction | default(omit) }}"
    interface: "{{ item.interface | default(omit) }}"
    interface_in: "{{ item.interface_in | default(omit) }}"
    interface_out: "{{ item.interface_out | default(omit) }}"
    route: "{{ item.route | default(omit) }}"
    comment: "{{ item.comment | default(omit) }}"
  loop: "{{ desktop_ufw_rules_effective }}"
  loop_control:
    label: "{{ item.name | default(item.port) }}"

- name: Enable UFW firewall on desktop when host rules are defined
  tags: [services, packages]
  community.general.ufw:
    state: enabled
  when: (desktop_ufw_rules_effective | default([])) | length > 0

- name: Check whether libvirt service directory exists
  tags: [packages, services]
  ansible.builtin.stat:
    path: /etc/sv/libvirtd
  register: libvirtd_service_dir
  when: "'void' in group_names"

- name: Enable libvirt daemon service
  tags: [packages, services]
  ansible.builtin.file:
    src: /etc/sv/libvirtd
    dest: /var/service/libvirtd
    state: link
  when:
    - "'void' in group_names"
    - libvirtd_service_dir.stat.exists

- name: Check virtualization group availability
  tags: [packages]
  ansible.builtin.getent:
    database: group
    key: "{{ item }}"
  loop:
    - kvm
    - libvirt
  loop_control:
    label: "{{ item }}"
  register: desktop_virtualization_group_state
  failed_when: false

- name: Add desktop user to virtualization groups
  tags: [packages]
  ansible.builtin.user:
    name: "{{ username }}"
    groups: "{{ item }}"
    append: true
  loop: >-
    {{
      desktop_virtualization_group_state.results
      | default([])
      | selectattr('failed', 'equalto', false)
      | selectattr('ansible_facts.getent_group', 'defined')
      | map(attribute='item')
      | list
    }}
  loop_control:
    label: "{{ item }}"

- name: Ensure emptty log directory exists
  tags: [packages, services, emptty]
  ansible.builtin.file:
    path: /var/log/emptty
    state: directory
    owner: root
    group: root
    mode: "0755"
  when: "'void' in group_names"

- name: Ensure emptty session directories exist
  tags: [packages, services, emptty]
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: root
    group: root
    mode: "0755"
  loop:
    - /etc/emptty/xsessions
  when: "'void' in group_names"

- name: Configure emptty
  tags: [packages, services, emptty]
  ansible.builtin.template:
    src: emptty-conf.j2
    dest: /etc/emptty/conf
    owner: root
    group: root
    mode: "0644"
  notify: Restart emptty
  when: "'void' in group_names"

- name: Copy common desktop dotfiles
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../dotfiles/desktop/{{ item.src }}"
    dest: "{{ user_home }}/{{ item.dest }}"
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "{{ item.mode }}"
  loop: >-
    {{
      (desktop_common_dotfiles | default([]))
      + ((desktop_void_dotfiles | default([])) if 'void' in group_names else [])
    }}
  loop_control:
    label: "{{ item.dest }}"

- name: Copy Emacs desktop dotfiles
  tags: [dotfiles, dotfiles:desktop, emacs]
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../dotfiles/desktop/{{ item.src }}"
    dest: "{{ user_home }}/{{ item.dest }}"
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "{{ item.mode }}"
  loop: "{{ desktop_emacs_dotfiles | default([]) }}"
  loop_control:
    label: "{{ item.dest }}"

- name: Render desktop templates with private values
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.template:
    src: "{{ item.src }}"
    dest: "{{ user_home }}/{{ item.dest }}"
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "{{ item.mode }}"
  loop:
    - src: desktop/.gitconfig.j2
      dest: .gitconfig
      mode: "0644"
    - src: desktop/.mbsyncrc.j2
      dest: .mbsyncrc
      mode: "0600"
    - src: desktop/.msmtprc.j2
      dest: .msmtprc
      mode: "0600"
    - src: desktop/email.el.j2
      dest: .emacs.d/lisp/misc/email.el
      mode: "0644"
  loop_control:
    label: "{{ item.dest }}"

- name: Define managed tmux plugin directories
  tags: [dotfiles, dotfiles:desktop, tmux]
  ansible.builtin.set_fact:
    tmux_managed_plugin_names:
      - tpm
      - tmux-sensible
      - tmux-autoreload
      - tmux-resurrect
      - tmux-continuum

- name: Check whether tmux plugin directories are git checkouts
  tags: [dotfiles, dotfiles:desktop, tmux]
  ansible.builtin.stat:
    path: "{{ user_home }}/.tmux/plugins/{{ item }}/.git"
  loop: "{{ tmux_managed_plugin_names }}"
  loop_control:
    label: "{{ item }}"
  register: tmux_plugin_git_state

- name: Remove stale vendored tmux plugin directories
  tags: [dotfiles, dotfiles:desktop, tmux]
  ansible.builtin.file:
    path: "{{ user_home }}/.tmux/plugins/{{ item.item }}"
    state: absent
  loop: "{{ tmux_plugin_git_state.results }}"
  loop_control:
    label: "{{ item.item }}"
  when:
    - item.stat.exists == false

- name: Bootstrap tmux plugin manager checkout
  tags: [dotfiles, dotfiles:desktop, tmux]
  ansible.builtin.git:
    repo: https://github.com/tmux-plugins/tpm
    dest: "{{ user_home }}/.tmux/plugins/tpm"
    version: master
    update: true
  become_user: "{{ username }}"
  environment:
    HOME: "{{ user_home }}"

- name: Install tmux plugins through TPM
  tags: [dotfiles, dotfiles:desktop, tmux]
  ansible.builtin.command:
    cmd: "{{ user_home }}/.tmux/plugins/tpm/bin/install_plugins"
  become_user: "{{ username }}"
  environment:
    HOME: "{{ user_home }}"
  register: tmux_plugin_install
  changed_when: >-
    (tmux_plugin_install.stdout | default('')) is search('download success') or
    (tmux_plugin_install.stderr | default('')) is search('download success')

- name: Refresh user font cache
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.command: fc-cache -f
  become_user: "{{ username }}"
  environment:
    HOME: "{{ user_home }}"
  changed_when: false

- name: Ensure .gnupg directory exists
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.file:
    path: "{{ user_home }}/.gnupg"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0700"

- name: Copy gpg-agent.conf
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.copy:
    src: "{{ playbook_dir }}/../dotfiles/desktop/.gnupg/gpg-agent.conf"
    dest: "{{ user_home }}/.gnupg/gpg-agent.conf"
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0600"

- name: Ensure local user directories exist
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.file:
    path: "{{ item.path }}"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "{{ item.mode }}"
  loop:
    - path: "{{ user_home }}/.local"
      mode: "0755"
    - path: "{{ user_home }}/.local/state"
      mode: "0755"
    - path: "{{ user_home }}/.cache"
      mode: "0755"
    - path: "{{ user_home }}/.cache/rclone"
      mode: "0755"
    - path: "{{ user_home }}/.local/state/ssh-agent"
      mode: "0700"
    - path: "{{ user_home }}/.local/share"
      mode: "0755"
    - path: "{{ user_home }}/.local/share/keyrings"
      mode: "0700"
    - path: "{{ user_home }}/Org"
      mode: "0755"
    - path: "{{ user_home }}/Remotes"
      mode: "0755"
    - path: /usr/src
      mode: "0755"

- name: Ensure maildir directories exist
  tags: [dotfiles, dotfiles:desktop]
  ansible.builtin.file:
    path: "{{ item }}"
    state: directory
    owner: "{{ username }}"
    group: "{{ user_group }}"
    mode: "0700"
  loop:
    - "{{ user_home }}/Maildir"
    - "{{ user_home }}/Maildir/iCloudAccount"
    - "{{ user_home }}/Maildir/ProtonMailAccount"

- name: Ensure flathub remote is configured
  tags: [packages]
  community.general.flatpak_remote:
    name: "{{ desktop_flatpak_remote_name | default('flathub') }}"
    state: present
    flatpakrepo_url: "{{ desktop_flatpak_remote_url | default('https://dl.flathub.org/repo/flathub.flatpakrepo') }}"
  when: (desktop_flatpak_packages | default([])) | length > 0

- name: Install desktop flatpak applications
  tags: [packages]
  community.general.flatpak:
    name: "{{ desktop_flatpak_packages }}"
    state: present
    remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
    method: system
  when: (desktop_flatpak_packages | default([])) | length > 0

- name: Install Flatpak extensions
  tags: [packages]
  community.general.flatpak:
    name: "{{ item }}"
    state: present
    remote: "{{ desktop_flatpak_remote_name | default('flathub') }}"
    method: system
  loop: "{{ desktop_flatpak_extensions | default([]) }}"
  when:
    - (desktop_flatpak_packages | default([])) | length > 0
    - (desktop_flatpak_extensions | default([])) | length > 0
    - item | length > 0

- name: Bootstrap rustup toolchain installer
  tags: [packages]
  block:
    - name: Check whether rustup is initialized
      ansible.builtin.stat:
        path: "{{ user_home }}/.cargo/bin/rustc"
      register: rustup_initialized

    - name: Run rustup-init
      ansible.builtin.command:
        cmd: rustup-init -y --no-modify-path
        creates: "{{ user_home }}/.cargo/bin/rustc"
      become_user: "{{ username }}"
      environment:
        HOME: "{{ user_home }}"
      when: not rustup_initialized.stat.exists

    - name: Ensure cargo env is sourced in shell profile
      ansible.builtin.lineinfile:
        path: "{{ user_home }}/.bashrc"
        regexp: '\. cargo/env'
        line: . "$HOME/.cargo/env"
        state: present
        create: true
        mode: "0644"
      become_user: "{{ username }}"
      when: not rustup_initialized.stat.exists
  when: desktop_source_tools | length > 0

- name: Build and install desktop source tools
  tags: [packages]
  ansible.builtin.include_tasks:
    file: source_tool.yml
    apply:
      tags: [packages]
  loop: "{{ desktop_source_tools + (desktop_void_source_tools | default([])) }}"
  when: (desktop_source_tools + (desktop_void_source_tools | default([]))) | length > 0
  loop_control:
    loop_var: source_tool
    label: "{{ source_tool.name }}"

- name: Set desktop tools tmp directory
  tags: [packages]
  ansible.builtin.set_fact:
    desktop_tools_tmp_dir: /tmp/desktop-tools

- name: Ensure temporary directory exists for desktop binary tools
  tags: [packages]
  ansible.builtin.file:
    path: "{{ desktop_tools_tmp_dir }}"
    state: directory
    mode: "0755"

- name: Install desktop binary tools
  tags: [packages]
  ansible.builtin.include_tasks:
    file: binary_tool.yml
    apply:
      tags: [packages]
  loop: "{{ desktop_binary_tools }}"
  when: desktop_binary_tools | length > 0
  loop_control:
    loop_var: binary_tool
    label: "{{ binary_tool.name }}"

- name: Install desktop npm packages
  tags: [packages, npm]
  community.general.npm:
    name: "{{ item.name }}"
    global: true
    state: "{{ item.state | default('present') }}"
  become: true
  loop: "{{ desktop_npm_packages | default([]) }}"
  when: desktop_npm_packages | length > 0
  loop_control:
    label: "{{ item.name }}"
