module IDRAC::Boot

def bios_error_prompt_disabled?

Check if BIOS error prompt is disabled
def bios_error_prompt_disabled?
  response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      if data["Attributes"] && data["Attributes"].has_key?("ErrPrompt")
        return data["Attributes"]["ErrPrompt"] == "Disabled"
      else
        debug "ErrPrompt attribute not found in BIOS settings", 1, :yellow
        return false
      end
    rescue JSON::ParserError
      debug "Failed to parse BIOS response", 0, :red
      return false
    end
  else
    debug "Failed to get BIOS information. Status code: #{response.status}", 0, :red
    return false
  end
end

def bios_hdd_placeholder_enabled?

def bios_hdd_placeholder_enabled?
  case self.license_version.to_i
  when 8
    # scp = usable_scp(get_system_configuration_profile(target: "BIOS"))
    # scp["BIOS.Setup.1-1"]["HddPlaceholder"] == "Enabled"
    true
  else
    response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios")
    json = JSON.parse(response.body)
    raise "Error reading HddPlaceholder setup" if json&.dig('Attributes','HddPlaceholder').blank?
    json["Attributes"]["HddPlaceholder"] == "Enabled"
  end
end

def bios_os_power_control_enabled?

def bios_os_power_control_enabled?
  case self.license_version.to_i
  when 8
    scp = usable_scp(get_system_configuration_profile(target: "BIOS"))
    scp["BIOS.Setup.1-1"]["ProcCStates"] == "Enabled" &&
      scp["BIOS.Setup.1-1"]["SysProfile"] == "PerfPerWattOptimizedOs" &&
      scp["BIOS.Setup.1-1"]["ProcPwrPerf"] == "OsDbpm"
  else
    response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios")
    json = JSON.parse(response.body)
    raise "Error reading PowerControl setup" if json&.dig('Attributes').blank?
    json["Attributes"]["ProcCStates"] == "Enabled" &&
      json["Attributes"]["SysProfile"] == "PerfPerWattOptimizedOs" &&
      json["Attributes"]["ProcPwrPerf"] == "OsDbpm"
  end
end

def configure_bios_settings(settings)

Configure BIOS settings
def configure_bios_settings(settings)
  response = authenticated_request(
    :patch, 
    "/redfish/v1/Systems/System.Embedded.1/Bios/Settings",
    body: { "Attributes": settings }.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  if response.status.between?(200, 299)
    puts "BIOS settings configured. A system reboot is required for changes to take effect.".green
    
    # Check if we need to wait for a job
    if response.headers["Location"]
      job_id = response.headers["Location"].split("/").last
      wait_for_job(job_id)
    end
    
    return true
  else
    error_message = "Failed to configure BIOS settings. Status code: #{response.status}"
    
    begin
      error_data = JSON.parse(response.body)
      if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"]
        error_info = error_data["error"]["@Message.ExtendedInfo"].first
        error_message += ", Message: #{error_info['Message']}"
      end
    rescue
      # Ignore JSON parsing errors
    end
    
    raise Error, error_message
  end
end

def create_scp_for_bios(settings)

Create System Configuration Profile for BIOS settings
def create_scp_for_bios(settings)
  attributes = []
  
  settings.each do |key, value|
    attributes << {
      "Name": key.to_s,
      "Value": value,
      "Set On Import": "True"
    }
  end
  
  scp = {
    "SystemConfiguration": {
      "Components": [
        {
          "FQDD": "BIOS.Setup.1-1",
          "Attributes": attributes
        }
      ]
    }
  }
  
  return scp
end

def ensure_uefi_boot

Ensure UEFI boot mode
def ensure_uefi_boot
  response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/Bios")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      
      if data["Attributes"]["BootMode"] == "Uefi"
        puts "System is already in UEFI boot mode".green
        return true
      else
        puts "System is not in UEFI boot mode. Setting to UEFI...".yellow
        
        # Create payload for UEFI boot mode
        payload = {
          "Attributes": {
            "BootMode": "Uefi"
          }
        }
        
        # If iDRAC 9, we need to enable HddPlaceholder
        if get_idrac_version == 9
          payload[:Attributes][:HddPlaceholder] = "Enabled"
        end
        
        response = authenticated_request(
          :patch, 
          "/redfish/v1/Systems/System.Embedded.1/Bios/Settings",
          body: payload.to_json,
          headers: { 'Content-Type': 'application/json' }
        )
        
        wait_for_job(response.headers["location"])
      end
    rescue JSON::ParserError
      raise Error, "Failed to parse BIOS response: #{response.body}"
    end
  else
    raise Error, "Failed to get BIOS information. Status code: #{response.status}"
  end
end

def get_bios_boot_options

Get BIOS boot options
def get_bios_boot_options
  response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/BootSources")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      
      if data["Attributes"]["UefiBootSeq"].blank?
        puts "Not in UEFI mode".red
        return false
      end
      
      boot_order = []
      boot_options = []
      
      data["Attributes"]["UefiBootSeq"].each do |seq|
        puts "#{seq["Name"]} > #{seq["Enabled"]}".yellow
        boot_options << seq["Name"]
        boot_order << seq["Name"] if seq["Enabled"]
      end
      
      return {
        boot_options: boot_options,
        boot_order: boot_order
      }
    rescue JSON::ParserError
      raise Error, "Failed to parse BIOS boot options response: #{response.body}"
    end
  else
    raise Error, "Failed to get BIOS boot options. Status code: #{response.status}"
  end
end

def get_idrac_version

Get iDRAC version - needed for boot management differences
def get_idrac_version
  response = authenticated_request(:get, "/redfish/v1")
  
  if response.status == 200
    begin
      data = JSON.parse(response.body)
      redfish = data["RedfishVersion"]
      server = response.headers["server"]
      
      case server.to_s.downcase
      when /appweb\/4.5.4/, /idrac\/8/
        return 8
      when /apache/, /idrac\/9/
        return 9
      else
        # Try to determine by RedfishVersion as fallback
        if redfish == "1.4.0"
          return 8
        elsif redfish == "1.18.0"
          return 9
        else
          raise Error, "Unknown iDRAC version: #{server} / #{redfish}"
        end
      end
    rescue JSON::ParserError
      raise Error, "Failed to parse iDRAC response: #{response.body}"
    end
  else
    raise Error, "Failed to get iDRAC information. Status code: #{response.status}"
  end
end

def import_system_configuration(scp, target: "ALL", reboot: false)

Import System Configuration Profile for advanced configurations
def import_system_configuration(scp, target: "ALL", reboot: false)
  params = {
    "ImportBuffer": JSON.pretty_generate(scp),
    "ShareParameters": {
      "Target": target
    }
  }
  # Configure shutdown behavior
  params["ShutdownType"] = "Forced"
  params["HostPowerState"] = reboot ? "On" : "Off"
  
  response = authenticated_request(
    :post, 
    "/redfish/v1/Managers/iDRAC.Embedded.1/Actions/Oem/EID_674_Manager.ImportSystemConfiguration",
    body: params.to_json,
    headers: { 'Content-Type': 'application/json' }
  )
  
  task = wait_for_task(response.headers["location"])
  debugger
  return task
end

def override_boot_source

Different approach for iDRAC 8 vs 9
This sets boot to HD but before that it sets the one-time boot to CD
def override_boot_source
  # For now try with all iDRAC versions
  if self.license_version.to_i == 9
    set_boot_order_hd_first()
    set_one_time_virtual_media_boot()
  else
    scp = {"FQDD"=>"iDRAC.Embedded.1", "Attributes"=> [{"Name"=>"ServerBoot.1#BootOnce", "Value"=>"Enabled", "Set On Import"=>"True"}, {"Name"=>"ServerBoot.1#FirstBootDevice", "Value"=>"VCD-DVD", "Set On Import"=>"True"}]}
    # set_uefi_boot_cd_once_then_hd
    # scp = self.set_bios_boot_cd_first
    # get_bios_boot_options # Make sure we know if the OS is calling it Unknown or RAID
    # {"FQDD"=>"BIOS.Setup.1-1", "Attributes"=>
    # [{"Name"=>"ServerBoot.1#BootOnce",       "Value"=>"Enabled", "Set On Import"=>"True"},
    # {"Name"=>"ServerBoot.1#FirstBootDevice", "Value"=>"VCD-DVD", "Set On Import"=>"True"},
    # {"Name"=>"BootSeqRetry",                 "Value"=>"Disabled", "Set On Import"=>"True"},
    # {"Name"=>"UefiBootSeq",                  "Value"=>"Unknown.Unknown.1-1,NIC.PxeDevice.1-1,Floppy.iDRACVirtual.1-1,Optical.iDRACVirtual.1-1",
    #  "Set On Import"=>"True"}]}
    # 3.3.0 :018 > scp1 = {"FQDD"=>"BIOS.Setup.1-1", "Attributes"=> [{"Name"=>"OneTimeUefiBootSeq", "Value"=>"VCD-DVD", "Set On Import"=>"True"}, {"Name"=>"BootSeqRetry", "Value"=>"Disabled", "Set On Import"=>"True"}, {"Name"=>"UefiBootSeq", "Value"=>"Unknown.Unknown.1-1,NIC.PxeDevice.1-1", "Set On Import"=>"True"}]}
    set_system_configuration_profile(scp) # This will cycle power and leave the device off.
  end
end

def scp_boot_mode_uefi(idrac_license_version: 9)

def scp_boot_mode_uefi(idrac_license_version: 9)
  opts = { "BootMode" => 'Uefi' }
  # If we're iDRAC 9, we need enable a placeholder, otherwise we can't order the
  # boot order until we've switched to UEFI mode.
  # Read [about it](https://dl.dell.com/manuals/all-products/esuprt_software/esuprt_it_ops_datcentr_mgmt/dell-management-solution-resources_white-papers12_en-us.pdf).
  # ...administrators may wish to reserve a boot entry for a fixed disk in the UEFI Boot Sequence before an OS is installed or before a physical or
  # virtual drive has been formatted. When a HardDisk Drive Placeholder is set to Enabled, the BIOS will create a boot option for the PERC RAID
  # (Integrated or in a PCIe slot) disk if a partition is found, even if there is no FAT filesystem present... this allows the Integrated RAID controller
  # to be moved in the UEFI Boot Sequence prior to the OS installation
  opts["HddPlaceholder"] = "Enabled" if idrac_license_version.to_i == 9
  self.make_scp(fqdd: "BIOS.Setup.1-1", attributes: opts)
end

def set_bios(hash)

https://infohub.delltechnologies.com/en-US/l/server-configuration-profiles-reference-guide/host-reboot-2/
What triggers a reboot?
def set_bios(hash)
  scp = self.make_scp(fqdd: "BIOS.Setup.1-1", attributes: hash)
  res = self.set_system_configuration_profile(scp)
  if res[:status] == :success
    self.get_bios_boot_options
  end
  res
end

def set_bios_ignore_errors(value = true)

Configure BIOS to ignore boot errors
def set_bios_ignore_errors(value = true)
  configure_bios_settings({
    "ErrPrompt": value ? "Disabled" : "Enabled"
  })
end

def set_bios_os_power_control

Configure BIOS to optimize for OS power management
def set_bios_os_power_control
  settings = {
    "ProcCStates": "Enabled",      # Processor C-States
    "SysProfile": "PerfPerWattOptimizedOs",
    "ProcPwrPerf": "OsDbpm",       # OS Power Management
    "PcieAspmL1": "Enabled"        # PCIe Active State Power Management
  }
  
  configure_bios_settings(settings)
end

def set_boot_order_hd_first

Set boot order (HD first)
def set_boot_order_hd_first
  # First ensure we're in UEFI mode
  ensure_uefi_boot
  
  # Get available boot options
  boot_options_response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1/BootOptions?$expand=*($levels=1)")
  
  if boot_options_response.status == 200
    begin
      data = JSON.parse(boot_options_response.body)
      
      puts "Available boot options:"
      data["Members"].each { |m| puts "\t#{m['DisplayName']} -> #{m['Id']}" }
      
      # Find RAID controller or HD
      device = data["Members"].find { |m| m["DisplayName"] =~ /RAID Controller/ }
      # Sometimes it's named differently
      device ||= data["Members"].find { |m| m["DisplayName"] =~ /ubuntu/i }
      device ||= data["Members"].find { |m| m["DisplayName"] =~ /UEFI Hard Drive/i }
      device ||= data["Members"].find { |m| m["DisplayName"] =~ /Hard Drive/i }
      
      if device.nil?
        raise Error, "No bootable hard drive or RAID controller found in boot options"
      end
      
      boot_id = device["Id"]
      
      # Set boot order
      response = authenticated_request(
        :patch, 
        "/redfish/v1/Systems/System.Embedded.1",
        body: { "Boot": { "BootOrder": [boot_id] } }.to_json,
        headers: { 'Content-Type': 'application/json' }
      )
      
      if response.status.between?(200, 299)
        puts "Boot order set to HD first".green
        return true
      else
        error_message = "Failed to set boot order. Status code: #{response.status}"
        
        begin
          error_data = JSON.parse(response.body)
          if error_data["error"] && error_data["error"]["@Message.ExtendedInfo"]
            error_info = error_data["error"]["@Message.ExtendedInfo"].first
            error_message += ", Message: #{error_info['Message']}"
          end
        rescue
          # Ignore JSON parsing errors
        end
        
        raise Error, error_message
      end
    rescue JSON::ParserError
      raise Error, "Failed to parse boot options response: #{response.body}"
    end
  else
    raise Error, "Failed to get boot options. Status code: #{boot_options_response.status}"
  end
end

def set_uefi_boot_cd_once_then_hd

def set_uefi_boot_cd_once_then_hd
  boot_options = get_bios_boot_options[:boot_options]
  # Note may have to put device into
  # self.set_bios( { "BootMode" => 'Uefi' } )
  # self.reboot!
  # And then reboot before you can make the following call:
  raid_name = boot_options.include?("RAID.Integrated.1-1") ? "RAID.Integrated.1-1" : "Unknown.Unknown.1-1"
  raise "No RAID HD in boot options" unless boot_options.include?(raid_name)
  bios = {
      "BootMode" => 'Uefi',
      "BootSeqRetry" => "Disabled",
      # "UefiTargetBootSourceOverride" => 'Cd',
      # "BootSourceOverrideTarget" => 'UefiTarget',
      # "OneTimeBootMode"       => "OneTimeUefiBootSeq",
      # One time boot order
      # "OneTimeHddSeqDev"      => "Optical.iDRACVirtual.1-1",
      # "OneTimeBiosBootSeqDev" => "Optical.iDRACVirtual.1-1",
      # "OneTimeUefiBootSeqDev" => "Optical.iDRACVirtual.1-1",
      # Enabled/Disabled Options
      # "SetBootOrderDis" => "Disk.USBBack.1-1",  # Don't boot to USB if it is plugged in
      "SetBootOrderEn"    => raid_name,
      # "SetBootOrderFqdd1" => raid_name,
      # "SetLegacyHddOrderFqdd1" => raid_name,
      # "SetBootOrderFqdd2" => "Optical.iDRACVirtual.1-1",
      # Permanent Boot Order
      "HddSeq"      => raid_name,
      "BiosBootSeq" => raid_name,
      "UefiBootSeq" => raid_name # This is likely redundant...
    }
  # The usb device will have 'usb' in it:
  usb_name = boot_options.select { |b| b =~ /usb/i }
  bios["SetBootOrderDis"] = usb_name if usb_name.present?
  set_bios(bios)
end