# frozen_string_literal: true

require 'open3'
require 'fileutils'
require 'tempfile'
require 'bolt/node/output'
require 'bolt/util'

module Bolt
  module Transport
    class Local < Simple
      class Connection
        attr_accessor :user, :logger, :target

        def initialize(target)
          @target = target
          # The familiar problem: Etc.getlogin is broken on osx
          @user = ENV['USER'] || Etc.getlogin
          @logger = Bolt::Logger.logger(self)
        end

        def shell
          @shell ||= if Bolt::Util.windows?
                       Bolt::Shell::Powershell.new(target, self)
                     else
                       Bolt::Shell::Bash.new(target, self)
                     end
        end

        def upload_file(source, dest)
          @logger.trace { "Uploading #{source} to #{dest}" }
          if source.is_a?(StringIO)
            Tempfile.create(File.basename(dest)) do |f|
              f.write(source.read)
              f.close
              FileUtils.mv(f, dest)
            end
          else
            # Mimic the behavior of `cp --remove-destination`
            # since the flag isn't supported on MacOS
            FileUtils.cp_r(source, dest, remove_destination: true)
          end
        rescue StandardError => e
          message = "Could not copy file to #{dest}: #{e}"
          raise Bolt::Node::FileError.new(message, 'COPY_ERROR')
        end

        def download_file(source, dest, _download)
          @logger.trace { "Downloading #{source} to #{dest}" }
          # Create the destination directory for the target, or the
          # copied file will have the target's name
          FileUtils.mkdir_p(dest)
          # Mimic the behavior of `cp --remove-destination`
          # since the flag isn't supported on MacOS
          FileUtils.cp_r(source, dest, remove_destination: true)
        rescue StandardError => e
          message = "Could not download file to #{dest}: #{e}"
          raise Bolt::Node::FileError.new(message, 'DOWNLOAD_ERROR')
        end

        def execute(command)
          if Bolt::Util.windows?
            # If it's already a powershell command then invoke it normally.
            # Otherwise, wrap it in powershell.exe.
            unless command.start_with?('powershell.exe')
              cmd = Bolt::Shell::Powershell::Snippets.exit_with_code(command)
              command = ['powershell.exe', *Bolt::Shell::Powershell::PS_ARGS, '-Command', cmd]
            end
          end

          Open3.popen3(*command)
        end

        # This is used by the Bash shell to decide whether to `cd` before
        # executing commands as a run-as user
        def reset_cwd?
          false
        end

        def max_command_length
          if Bolt::Util.windows?
            32000
          end
        end
      end
    end
  end
end