class IDRAC::Client

def authenticated_request(method, path, options = {}, retry_count = 0)

Make an authenticated request to the iDRAC
def authenticated_request(method, path, options = {}, retry_count = 0)
  # Limit retries to prevent infinite loops
  if retry_count >= 3
    puts "Maximum retry count reached for authenticated request".red.bold
    raise Error, "Maximum retry count reached for authenticated request"
  end
  
  # If we're in direct mode, use Basic Auth
  if @direct_mode
    # Create Basic Auth header
    auth_header = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
    
    # Add the Authorization header to the request
    options[:headers] ||= {}
    options[:headers]['Authorization'] = auth_header
    
    # Make the request
    begin
      response = connection.send(method, path) do |req|
        req.headers.merge!(options[:headers])
        req.body = options[:body] if options[:body]
      end
      
      return response
    rescue => e
      puts "Error during authenticated request (direct mode): #{e.message}".red.bold
      raise Error, "Error during authenticated request: #{e.message}"
    end
  else
    # Use X-Auth-Token if available
    if @x_auth_token
      # Add the X-Auth-Token header to the request
      options[:headers] ||= {}
      options[:headers]['X-Auth-Token'] = @x_auth_token
      
      # Make the request
      begin
        response = connection.send(method, path) do |req|
          req.headers.merge!(options[:headers])
          req.body = options[:body] if options[:body]
        end
        
        # Check if the session is still valid
        if response.status == 401 || response.status == 403
          puts "Session expired or invalid, attempting to create a new session...".light_yellow
          
          # Try to create a new session
          if create_redfish_session
            puts "Successfully created a new session, retrying request...".green
            return authenticated_request(method, path, options, retry_count + 1)
          else
            puts "Failed to create a new session, falling back to direct mode...".light_yellow
            @direct_mode = true
            return authenticated_request(method, path, options, retry_count + 1)
          end
        end
        
        return response
      rescue => e
        puts "Error during authenticated request (token mode): #{e.message}".red.bold
        
        # Try to create a new session
        if create_redfish_session
          puts "Successfully created a new session after error, retrying request...".green
          return authenticated_request(method, path, options, retry_count + 1)
        else
          puts "Failed to create a new session after error, falling back to direct mode...".light_yellow
          @direct_mode = true
          return authenticated_request(method, path, options, retry_count + 1)
        end
      end
    else
      # If we don't have a token, try to create a session
      if create_redfish_session
        puts "Successfully created a new session, making request...".green
        return authenticated_request(method, path, options, retry_count + 1)
      else
        puts "Failed to create a session, falling back to direct mode...".light_yellow
        @direct_mode = true
        return authenticated_request(method, path, options, retry_count + 1)
      end
    end
  end
end

def base_url

def base_url
  protocol = use_ssl ? 'https' : 'http'
  "#{protocol}://#{host}:#{port}"
end

def connection

def connection
  @connection ||= Faraday.new(url: base_url, ssl: { verify: verify_ssl }) do |faraday|
    faraday.request :multipart
    faraday.request :url_encoded
    faraday.adapter Faraday.default_adapter
  end
end

def create_redfish_session

Create a Redfish session
def create_redfish_session
  # Skip if we're in direct mode
  if @direct_mode
    puts "Skipping Redfish session creation (direct mode)".light_yellow
    return false
  end
  
  url = '/redfish/v1/SessionService/Sessions'
  payload = { "UserName" => username, "Password" => password }
  
  # Try first with just Content-Type header (no Basic Auth)
  begin
    response = connection.post(url) do |req|
      req.headers['Content-Type'] = 'application/json'
      req.body = payload.to_json
    end
    
    if response.status == 201 || response.status == 200
      # Extract X-Auth-Token from response headers
      @x_auth_token = response.headers['X-Auth-Token']
      
      # Extract session location from response headers
      @session_location = response.headers['Location']
      
      puts "Redfish session created successfully".green
      @sessions_maxed = false
      return true
    end
  rescue => e
    puts "First session creation attempt failed: #{e.message}".light_red
  end
  
  # If that fails, try with Basic Auth
  begin
    # Use Basic Auth for the session creation
    basic_auth_headers = {
      'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
      'Content-Type' => 'application/json'
    }
    
    response = connection.post(url) do |req|
      req.headers.merge!(basic_auth_headers)
      req.body = payload.to_json
    end
    
    if response.status == 201 || response.status == 200
      # Extract X-Auth-Token from response headers
      @x_auth_token = response.headers['X-Auth-Token']
      
      # Extract session location from response headers
      @session_location = response.headers['Location']
      
      puts "Redfish session created successfully with Basic Auth".green
      @sessions_maxed = false
      return true
    elsif response.status == 400 && response.body.include?("maximum number of user sessions")
      puts "Maximum sessions reached during Redfish session creation".light_red
      @sessions_maxed = true
      
      # Try to clear sessions if auto_delete_sessions is enabled
      if @auto_delete_sessions
        puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
        if force_clear_sessions
          puts "Successfully cleared sessions, trying to create a new session".green
          
          # Try one more time after clearing
          response = connection.post(url) do |req|
            req.headers.merge!(basic_auth_headers)
            req.body = payload.to_json
          end
          
          if response.status == 201 || response.status == 200
            @x_auth_token = response.headers['X-Auth-Token']
            @session_location = response.headers['Location']
            puts "Redfish session created successfully after clearing sessions".green
            @sessions_maxed = false
            return true
          else
            puts "Failed to create Redfish session after clearing: #{response.status} - #{response.body}".red
            # If we still can't create a session, switch to direct mode
            @direct_mode = true
            return false
          end
        else
          puts "Failed to clear sessions, switching to direct mode".light_yellow
          @direct_mode = true
          return false
        end
      else
        puts "Auto-delete sessions is disabled, switching to direct mode".light_yellow
        @direct_mode = true
        return false
      end
    else
      puts "Failed to create Redfish session: #{response.status} - #{response.body}".red
      
      # If we get a 415 error, try with form-urlencoded
      if response.status == 415
        puts "Trying with form-urlencoded content type".light_cyan
        response = connection.post(url) do |req|
          req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
          req.headers['Authorization'] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
          req.body = "UserName=#{URI.encode_www_form_component(username)}&Password=#{URI.encode_www_form_component(password)}"
        end
        
        if response.status == 201 || response.status == 200
          @x_auth_token = response.headers['X-Auth-Token']
          @session_location = response.headers['Location']
          puts "Redfish session created successfully with form-urlencoded".green
          @sessions_maxed = false
          return true
        else
          puts "Failed with form-urlencoded too: #{response.status} - #{response.body}".red
          @direct_mode = true
          return false
        end
      else
        @direct_mode = true
        return false
      end
    end
  rescue => e
    puts "Error during Redfish session creation: #{e.message}".red.bold
    @direct_mode = true
    return false
  end
end

def delete_all_sessions_with_basic_auth

Delete all sessions using Basic Authentication
def delete_all_sessions_with_basic_auth
  puts "Attempting to delete all sessions using Basic Authentication...".light_cyan
  
  # First, get the list of sessions
  sessions_url = '/redfish/v1/SessionService/Sessions'
  
  # Create a connection with Basic Auth
  basic_auth_headers = {
    'Authorization' => "Basic #{Base64.strict_encode64("#{username}:#{password}")}",
    'Content-Type' => 'application/json'
  }
  
  begin
    # Get the list of sessions
    response = connection.get(sessions_url) do |req|
      req.headers.merge!(basic_auth_headers)
    end
    
    if response.status != 200
      puts "Failed to get sessions list: #{response.status} - #{response.body}".red
      return false
    end
    
    # Parse the response to get session IDs
    begin
      sessions_data = JSON.parse(response.body)
      
      if sessions_data['Members'] && sessions_data['Members'].any?
        puts "Found #{sessions_data['Members'].count} active sessions".light_yellow
        
        # Delete each session
        success = true
        sessions_data['Members'].each do |session|
          session_url = session['@odata.id']
          
          # Skip if no URL
          next unless session_url
          
          # Delete the session
          delete_response = connection.delete(session_url) do |req|
            req.headers.merge!(basic_auth_headers)
          end
          
          if delete_response.status == 200 || delete_response.status == 204
            puts "Successfully deleted session: #{session_url}".green
          else
            puts "Failed to delete session #{session_url}: #{delete_response.status}".red
            success = false
          end
          
          # Small delay between deletions
          sleep(1)
        end
        
        return success
      else
        puts "No active sessions found".light_yellow
        return true
      end
    rescue JSON::ParserError => e
      puts "Error parsing sessions response: #{e.message}".red.bold
      return false
    end
  rescue => e
    puts "Error during session deletion with Basic Auth: #{e.message}".red.bold
    return false
  end
end

def delete_redfish_session

Delete the Redfish session
def delete_redfish_session
  return unless @x_auth_token && @session_location
  
  begin
    puts "Deleting Redfish session...".light_cyan
    
    # Use the X-Auth-Token for authentication
    headers = { 'X-Auth-Token' => @x_auth_token }
    
    response = connection.delete(@session_location) do |req|
      req.headers.merge!(headers)
    end
    
    if response.status == 200 || response.status == 204
      puts "Redfish session deleted successfully".green
      @x_auth_token = nil
      @session_location = nil
      return true
    else
      puts "Failed to delete Redfish session: #{response.status} - #{response.body}".red
      return false
    end
  rescue => e
    puts "Error during Redfish session deletion: #{e.message}".red.bold
    return false
  end
end

def force_clear_sessions

Force clear all sessions by directly using Basic Auth
def force_clear_sessions
  puts "Attempting to force clear all sessions...".light_cyan
  
  # Try to delete sessions directly using Basic Auth
  if delete_all_sessions_with_basic_auth
    puts "Successfully cleared sessions using Basic Auth".green
    return true
  else
    puts "Failed to clear sessions using Basic Auth".red
    return false
  end
end

def get(path:, headers: {})

def get(path:, headers: {})
  # For screenshot functionality, we need to use the WebUI cookies
  if @cookies.nil? && path.include?('screen/screen.jpg')
    webui_login unless @session_id
  end
  
  headers_to_use = {
    "User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
    "Accept-Encoding" => "deflate, gzip"
  }
  
  if @cookies
    headers_to_use["Cookie"] = @cookies
  elsif @direct_mode
    # In direct mode, use Basic Auth
    headers_to_use["Authorization"] = "Basic #{Base64.strict_encode64("#{username}:#{password}")}"
  elsif @x_auth_token
    headers_to_use["X-Auth-Token"] = @x_auth_token
  end
  
  response = HTTParty.get(
    "#{base_url}/#{path}",
    headers: headers_to_use.merge(headers),
    verify: false
  )
end

def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true, direct_mode: false, auto_delete_sessions: true)

def initialize(host:, username:, password:, port: 443, use_ssl: true, verify_ssl: true, direct_mode: false, auto_delete_sessions: true)
  @host = host
  @username = username
  @password = password
  @port = port
  @use_ssl = use_ssl
  @verify_ssl = verify_ssl
  @session_id = nil
  @cookies = nil
  @x_auth_token = nil
  @direct_mode = direct_mode
  @sessions_maxed = false
  @tried_clearing_sessions = false
  @auto_delete_sessions = auto_delete_sessions
end

def login

Login to iDRAC
def login
  # If we're in direct mode, skip login attempts
  if @direct_mode
    puts "Using direct mode (Basic Auth) for all requests".light_yellow
    return true
  end
  
  # Try to create a Redfish session
  if create_redfish_session
    puts "Successfully logged in to iDRAC using Redfish session".green
    return true
  else
    if @sessions_maxed
      puts "Maximum sessions reached and could not clear sessions".light_red
    else
      puts "Failed to create Redfish session, falling back to direct mode".light_yellow
    end
    
    @direct_mode = true
    return true
  end
end

def logout

Logout from iDRAC
def logout
  if @x_auth_token
    delete_redfish_session
  end
  
  if @session_id
    webui_logout
  end
  
  puts "Logged out from iDRAC".green
  return true
end

def screenshot

def screenshot
  # Create a Screenshot instance and capture a screenshot
  screenshot_instance = Screenshot.new(self)
  screenshot_instance.capture
end

def webui_login(retry_count = 0)

Login to the WebUI (for screenshot functionality)
def webui_login(retry_count = 0)
  # Limit retries to prevent infinite loops
  if retry_count >= 3
    puts "Maximum retry count reached for WebUI login".red
    return false
  end
  
  # Skip if we already have a session ID
  return true if @session_id
  
  begin
    puts "Logging in to WebUI...".light_cyan
    
    # Create the login URL
    login_url = "#{base_url}/data/login"
    
    # Create the login payload
    payload = {
      'user' => username,
      'password' => password
    }
    
    # Make the login request
    response = HTTParty.post(
      login_url,
      body: payload,
      verify: verify_ssl,
      headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }
    )
    
    # Check if the login was successful
    if response.code == 200
      # Extract the session ID from the response
      if response.body.include?('ST2')
        @session_id = response.body.match(/ST2=([^;]+)/)[1]
        @cookies = response.headers['set-cookie']
        puts "WebUI login successful".green
        return true
      else
        puts "WebUI login failed: No session ID found in response".red
        return false
      end
    elsif response.code == 400 && response.body.include?("maximum number of user sessions")
      puts "Maximum sessions reached during WebUI login".light_red
      
      # Try to clear sessions if auto_delete_sessions is enabled
      if @auto_delete_sessions && !@tried_clearing_sessions
        puts "Auto-delete sessions is enabled, attempting to clear sessions".light_cyan
        @tried_clearing_sessions = true
        
        if force_clear_sessions
          puts "Successfully cleared sessions, trying WebUI login again".green
          return webui_login(retry_count + 1)
        else
          puts "Failed to clear sessions for WebUI login".red
          return false
        end
      else
        puts "Auto-delete sessions is disabled or already tried clearing".light_yellow
        return false
      end
    else
      puts "WebUI login failed: #{response.code} - #{response.body}".red
      return false
    end
  rescue => e
    puts "Error during WebUI login: #{e.message}".red.bold
    return false
  end
end

def webui_logout

Logout from the WebUI
def webui_logout
  return unless @session_id
  
  begin
    puts "Logging out from WebUI...".light_cyan
    
    # Create the logout URL
    logout_url = "#{base_url}/data/logout"
    
    # Make the logout request
    response = HTTParty.get(
      logout_url,
      verify: verify_ssl,
      headers: { 'Cookie' => @cookies }
    )
    
    # Check if the logout was successful
    if response.code == 200
      puts "WebUI logout successful".green
      @session_id = nil
      @cookies = nil
      return true
    else
      puts "WebUI logout failed: #{response.code} - #{response.body}".red
      return false
    end
  rescue => e
    puts "Error during WebUI logout: #{e.message}".red.bold
    return false
  end
end