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

require "fileutils"
require "shellwords"

require_relative "../../../synced_folders/unix_mount_helpers"

module VagrantPlugins
  module GuestLinux
    module Cap
      class MountSMBSharedFolder

        extend SyncedFolder::UnixMountHelpers

        # Mounts and SMB folder on linux guest
        #
        # @param [Machine] machine
        # @param [String] name of mount
        # @param [String] path of mount on guest
        # @param [Hash] hash of mount options 
        def self.mount_smb_shared_folder(machine, name, guestpath, options)
          expanded_guest_path = machine.guest.capability(
            :shell_expand_guest_path, guestpath)
          options[:smb_id] ||= name

          mount_device = options[:plugin].capability(:mount_name, name, options)
          mount_options, _, _ = options[:plugin].capability(
            :mount_options, name, expanded_guest_path, options)
          mount_type = options[:plugin].capability(:mount_type)
          # If a domain is provided in the username, separate it
          username, domain = (options[:smb_username] || '').split('@', 2)
          smb_password = options[:smb_password]
          # Ensure password is scrubbed
          Vagrant::Util::CredentialScrubber.sensitive(smb_password)
        
          if mount_options.include?("mfsymlinks")
            display_mfsymlinks_warning(machine.env)
          end
          
          mount_command = "mount -t #{mount_type} -o #{mount_options} #{mount_device} #{expanded_guest_path}"

          # Create the guest path if it doesn't exist
          machine.communicate.sudo("mkdir -p #{expanded_guest_path}")

          # Write the credentials file
          machine.communicate.sudo(<<-SCRIPT)
cat <<"EOF" >/etc/smb_creds_#{name}
username=#{username}
password=#{smb_password}
#{domain ? "domain=#{domain}" : ""}
EOF
chmod 0600 /etc/smb_creds_#{name}
SCRIPT

          # Attempt to mount the folder. We retry here a few times because
          # it can fail early on.
          begin
            retryable(on: Vagrant::Errors::LinuxMountFailed, tries: 10, sleep: 2) do
              no_such_device = false
              stderr = ""
              status = machine.communicate.sudo(mount_command, error_check: false) do |type, data|
                if type == :stderr
                  no_such_device = true if data =~ /No such device/i
                  stderr += data.to_s
                end
              end
              if status != 0 || no_such_device
                raise Vagrant::Errors::LinuxMountFailed,
                  command: mount_command,
                  output: stderr
              end
            end
          ensure
            # Always remove credentials file after mounting attempts
            # have been completed
            if !machine.config.vm.allow_fstab_modification
              machine.communicate.sudo("rm /etc/smb_creds_#{name}")
            end
          end

          emit_upstart_notification(machine, expanded_guest_path)
        end

        def self.display_mfsymlinks_warning(env)
          d_file = env.data_dir.join("mfsymlinks_warning")
          if !d_file.exist?
            FileUtils.touch(d_file.to_path)
            env.ui.warn(I18n.t("vagrant.actions.vm.smb.mfsymlink_warning"))
          end
        end
      end
    end
  end
end
