# frozen_string_literal: true

require 'rake_factory'
require 'ruby_git_crypt'
require 'ruby_gpg2'

require_relative '../home'
require_relative '../template'
require_relative '../mixins/support'

module RakeGitCrypt
  module Tasks
    class AddUser < RakeFactory::Task
      include Mixins::Support

      default_name :add_user
      default_description 'Add user to git-crypt.'

      parameter :key_name

      parameter :allow_git_crypt_commit, default: false
      parameter :allow_untrusted_keys, default: false

      parameter :gpg_user_id
      parameter :gpg_user_key_path

      parameter :gpg_home_directory
      parameter :gpg_work_directory, default: '/tmp'

      parameter :commit_message_template,
                default: 'Adding git-crypt GPG user with <%= @type %>: ' \
                         "'<%= @value %>'."

      parameter :commit_task_name

      action do |task, args|
        validate

        if gpg_user_id
          log_adding_by_id
          add_gpg_user(gpg_home_directory, gpg_user_id)
        elsif gpg_user_key_path
          log_adding_by_key_path
          with_gpg_home_directory do |home_directory|
            result = import_key(home_directory)
            key_fingerprint = lookup_key_fingerprint(result)
            add_gpg_user(home_directory, key_fingerprint,
                         auto_trust: gpg_home_directory.nil?)
          end
        end

        maybe_commit(task, args)
        log_done
      end

      private

      def with_gpg_home_directory(&block)
        Home.new(gpg_work_directory, gpg_home_directory || :temporary)
            .with_resolved_directory do |home_directory|
          block.call(home_directory)
        end
      end

      def validate
        return if gpg_user_id || gpg_user_key_path

        raise RakeFactory::RequiredParameterUnset,
              'One of gpg_user_id or gpg_user_key_path must be provided ' \
              'but neither was.'
      end

      def import_key(gpg_home_directory)
        RubyGPG2.import(
          key_file_paths: [gpg_user_key_path],
          work_directory: gpg_work_directory,
          home_directory: gpg_home_directory,
          with_status: true
        )
      end

      def lookup_key_fingerprint(result)
        result.status.filter_by_type(:import_ok)
              .first_line.key_fingerprint
      end

      def add_gpg_user(gpg_home_directory, gpg_user_id, auto_trust: false)
        RubyGitCrypt.add_gpg_user(
          {
            gpg_user_id:,
            key_name:,
            no_commit: !allow_git_crypt_commit,
            trusted: auto_trust || allow_untrusted_keys
          },
          { environment: git_crypt_environment(gpg_home_directory) }
        )
      end

      def maybe_commit(task, args)
        return unless commit_task_name

        invoke_and_reenable_task_with_name(
          task, commit_task_name,
          [commit_message(task), *args]
        )
      end

      def commit_message(task)
        Template.new(commit_message_template)
                .render(type: gpg_user_id ? 'ID' : 'key path',
                        value: gpg_user_id || gpg_user_key_path,
                        task:)
      end

      def git_crypt_environment(gpg_home_directory)
        gpg_home_directory ? { GNUPGHOME: gpg_home_directory } : {}
      end

      def log_adding_by_id
        $stdout.puts(
          "Adding GPG user with ID: '#{gpg_user_id}' to git-crypt..."
        )
      end

      def log_adding_by_key_path
        $stdout.puts(
          "Adding GPG user with key at: '#{gpg_user_key_path}' to git-crypt..."
        )
      end

      def log_done
        $stdout.puts('Done.')
      end
    end
  end
end