# frozen_string_literal: true

module Bosh::AzureCloud
  class VMManager
    private

    def _get_availability_set_name(vm_props, env)
      availability_set_name = vm_props.availability_set.name
      if availability_set_name.nil? && !(env.nil? || env['bosh'].nil? || env['bosh']['group'].nil?)
        availability_set_name = env['bosh']['group']

        # https://github.com/cloudfoundry/bosh-azure-cpi-release/issues/209
        # On Azure the length of the availability set name must be between 1 and 80 characters.
        # The group name which is generated by BOSH director may be too long.
        # CPI will truncate the name to below format if the length is greater than 80.
        # az-MD5-[LAST-40-CHARACTERS-OF-GROUP]
        availability_set_name = "az-#{Digest::MD5.hexdigest(availability_set_name)}-#{availability_set_name[-40..]}" if availability_set_name.length > 80
      end

      availability_set_name
    end

    def _get_or_create_availability_set(resource_group_name, availability_set_name, vm_props, location)
      return nil if availability_set_name.nil?

      availability_set_params = {
        name: availability_set_name,
        location: location,
        tags: AZURE_TAGS,
        platform_update_domain_count: vm_props.availability_set.platform_update_domain_count,
        platform_fault_domain_count: vm_props.availability_set.platform_fault_domain_count,
        managed: @use_managed_disks
      }
      availability_set = nil
      flock("#{CPI_LOCK_PREFIX_AVAILABILITY_SET}-#{availability_set_name}", File::LOCK_EX) do
        availability_set = @azure_client.get_availability_set_by_name(resource_group_name, availability_set_name)
        if availability_set.nil?
          @logger.info("availability set '#{availability_set_name}' doesn't exist. Will create a new one.")
          @azure_client.create_availability_set(resource_group_name, availability_set_params)
          availability_set = @azure_client.get_availability_set_by_name(resource_group_name, availability_set_name)
        # In some regions, the location of availability set is case-sensitive, e.g. CanadaCentral instead of canadacentral.
        elsif !availability_set[:location].casecmp(availability_set_params[:location]).zero?
          cloud_error("availability set '#{availability_set_name}' already exists, but in a different location '#{availability_set[:location].downcase}' instead of '#{availability_set_params[:location].downcase}'. Please delete the availability set or choose another location.")
        elsif !@use_managed_disks && availability_set[:managed]
          cloud_error("availability set '#{availability_set_name}' already exists. It's not allowed to update it from managed to unmanaged.")
        elsif @use_managed_disks && !availability_set[:managed]
          @logger.info("availability set '#{availability_set_name}' exists, but it needs to be updated from unmanaged to managed.")
          maximum_fault_domain_count = [availability_set[:platform_fault_domain_count], @azure_client.get_max_fault_domains_for_location(location)].compact.min
          availability_set_params.merge!(
            platform_update_domain_count: availability_set[:platform_update_domain_count],
            platform_fault_domain_count: maximum_fault_domain_count,
            managed: true
          )
          @azure_client.create_availability_set(resource_group_name, availability_set_params)
          availability_set = @azure_client.get_availability_set_by_name(resource_group_name, availability_set_name)
        else
          @logger.info("availability set '#{availability_set_name}' exists. No need to update.")
        end
      end
      cloud_error("availability set '#{availability_set_name}' is not created.") if availability_set.nil?
      availability_set
    end

    def _delete_empty_availability_set(resource_group_name, availability_set_name)
      flock("#{CPI_LOCK_PREFIX_AVAILABILITY_SET}-#{availability_set_name}", File::LOCK_EX | File::LOCK_NB) do
        availability_set = @azure_client.get_availability_set_by_name(resource_group_name, availability_set_name)
        @azure_client.delete_availability_set(resource_group_name, availability_set_name) if availability_set && availability_set[:virtual_machines].empty?
      end
    end
  end
end
