# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: BUSL-1.1

require "tempfile"

require_relative '../../../communicators/winssh/communicator'

module VagrantPlugins
  module GuestWindows
    module Cap
      class PublicKey
        def self.insert_public_key(machine, contents)
          if machine.communicate.is_a?(CommunicatorWinSSH::Communicator)
            contents = contents.strip
            winssh_modify_authorized_keys machine do |keys|
              if !keys.include?(contents)
                keys << contents
              end
            end
          else
            raise Vagrant::Errors::SSHInsertKeyUnsupported
          end
        end

        def self.remove_public_key(machine, contents)
          if machine.communicate.is_a?(CommunicatorWinSSH::Communicator)
            winssh_modify_authorized_keys machine do |keys|
              keys.delete(contents)
            end
          else
            raise Vagrant::Errors::SSHInsertKeyUnsupported
          end
        end

        def self.winssh_modify_authorized_keys(machine)
          comm = machine.communicate
          directories = fetch_guest_paths(comm)
          home_dir = directories[:home]
          temp_dir = directories[:temp]

          # Ensure the user's ssh directory exists
          remote_ssh_dir = "#{home_dir}\\.ssh"
          comm.execute("New-Item -Path '#{remote_ssh_dir}' -ItemType directory -Force", shell: "powershell")
          remote_upload_path = "#{temp_dir}\\vagrant-insert-pubkey-#{Time.now.to_i}"
          remote_authkeys_path = "#{remote_ssh_dir}\\authorized_keys"

          keys_file = Tempfile.new("vagrant-windows-insert-public-key")
          keys_file.close
          # Check if an authorized_keys file already exists
          result = comm.execute("dir \"#{remote_authkeys_path}\"", shell: "cmd", error_check: false)
          if result == 0
            comm.download(remote_authkeys_path, keys_file.path)
            keys = File.read(keys_file.path).split(/[\r\n]+/)
          else
            keys = []
          end
          yield keys
          File.write(keys_file.path, keys.join("\r\n") + "\r\n")
          comm.upload(keys_file.path, remote_upload_path)
          keys_file.delete
          comm.execute(<<-EOC.gsub(/^\s*/, ""), shell: "powershell")
            Set-Acl "#{remote_upload_path}" (Get-Acl "#{remote_authkeys_path}")
            Move-Item -Force "#{remote_upload_path}" "#{remote_authkeys_path}"
          EOC
        end

        # Fetch user's temporary and home directory paths from the Windows guest
        #
        # @param [Communicator]
        # @return [Hash] {:temp, :home}
        def self.fetch_guest_paths(communicator)
          output = ""
          communicator.execute("Write-Output $env:TEMP\nWrite-Output $env:USERPROFILE", shell: "powershell") do |type, data|
            if type == :stdout
              output << data
            end
          end
          temp_dir, home_dir = output.strip.split(/[\r\n]+/)
          if temp_dir.nil? || home_dir.nil?
            raise Errors::PublicKeyDirectoryFailure
          end
          {temp: temp_dir, home: home_dir}
        end
      end
    end
  end
end
