lib/idrac/license.rb



module IDRAC
  module License
    # Gets the license information from the iDRAC
    # @return [Hash] License details
    def license_info
      # Try the standard license endpoint first (works in iDRAC 9+)
      response = authenticated_request(:get, "/redfish/v1/LicenseService/Licenses")
      
      if response.status == 200
        license_data = JSON.parse(response.body)
        debug "License collection: #{license_data}", 2

        # Check if there are any license entries
        if !license_data["Members"] || license_data["Members"].empty?
          debug "No licenses found", 1, :yellow
          return try_dell_oem_license_path()
        end

        # Get the first license in the list
        license_uri = license_data["Members"][0]["@odata.id"]
        debug "Using license URI: #{license_uri}", 2

        # Get detailed license information
        license_response = authenticated_request(:get, license_uri)
        if license_response.status != 200
          debug "Failed to retrieve license details: #{license_response.status}", 1, :red
          return try_dell_oem_license_path()
        end

        license_details = JSON.parse(license_response.body)
        debug "License details: #{license_details}", 2

        return license_details
      else
        # The endpoint is not available (probably iDRAC 8)
        debug "Standard license endpoint failed: #{response.status}, trying Dell OEM path", 1, :yellow
        return try_dell_oem_license_path()
      end
    end

    # Extracts the iDRAC version from the license description or server header
    # @return [Integer, nil] The license version (e.g. 9) or nil if not found
    def license_version
      # First try to get from license info
      license = license_info
      if license
        # Check the Description field, which often contains the version
        # Example: "iDRAC9 Enterprise License"
        if license["Description"]&.match(/iDRAC(\d+)/i)
          version = license["Description"].match(/iDRAC(\d+)/i)[1].to_i
          debug "Found license version from Description: #{version}", 1
          return version
        end

        # Try alternative fields if Description didn't work
        if license["Name"]&.match(/iDRAC(\d+)/i)
          version = license["Name"].match(/iDRAC(\d+)/i)[1].to_i
          debug "Found license version from Name: #{version}", 1
          return version
        end
        
        # For Dell OEM license response format
        if license["LicenseDescription"]&.match(/iDRAC(\d+)/i)
          version = license["LicenseDescription"].match(/iDRAC(\d+)/i)[1].to_i
          debug "Found license version from LicenseDescription: #{version}", 1
          return version
        end
      end
      
      # If license info failed or didn't have version info, try to get from server header
      # Make a simple request to check the server header (often contains iDRAC version)
      response = authenticated_request(:get, "/redfish/v1")
      if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
        version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
        debug "Found license version from server header: #{version}", 1
        return version
      end
      
      debug "Could not determine license version from license info or server header", 1, :yellow
      nil
    end
    
    private
    
    # Attempt to get license information using Dell OEM extension path (for iDRAC 8)
    # @return [Hash, nil] License info or nil if not found
    def try_dell_oem_license_path
      # Try several potential Dell license paths (order matters - most likely first)
      dell_license_paths = [
        "/redfish/v1/Managers/iDRAC.Embedded.1/Oem/Dell/DellLicenseManagementService/Licenses",
        "/redfish/v1/Managers/iDRAC.Embedded.1/Attributes", # iDRAC attributes might contain license info
        "/redfish/v1/Managers/iDRAC.Embedded.1"             # Manager entity might have license info embedded
      ]
      
      dell_license_paths.each do |path|
        response = authenticated_request(:get, path)
        
        if response.status == 200
          debug "Found valid Dell license path: #{path}", 2
          data = JSON.parse(response.body)
          
          # Check for license info in this response based on the path
          if path.include?("DellLicenseManagementService")
            return handle_dell_license_service_response(data)
          elsif path.include?("Attributes")
            return handle_dell_attributes_response(data)
          elsif path.include?("iDRAC.Embedded.1") && !path.include?("Attributes")
            return handle_dell_manager_response(data)
          end
        else
          debug "Dell path #{path} response status: #{response.status}", 3
        end
      end
      
      # If we couldn't find any API path that works, try the service tag detection method 
      service_tag = get_service_tag
      if service_tag
        # Service tag is often used to indicate Enterprise licenses on Dell systems
        license_type = determine_license_type()
        return {
          "Id" => "iDRAC-License",
          "Description" => "iDRAC8 #{license_type} License",
          "Name" => "iDRAC License",
          "LicenseType" => license_type,
          "Status" => { "Health" => "OK" },
          "Removable" => false,
          "EntitlementID" => service_tag # Dell often uses service tag as entitlement ID
        }
      end
      
      # Fall back to feature detection if all else fails
      debug "All Dell OEM license paths failed, using fallback detection", 1, :yellow
      return create_fallback_license_info()
    end
    
    # Handle response from Dell License Management Service
    def handle_dell_license_service_response(data)
      # Check if there are any license entries
      if !data["Members"] || data["Members"].empty?
        debug "No licenses found in Dell OEM path", 1, :yellow
        return create_fallback_license_info(use_basic: true)
      end
      
      # Get the first license in the list
      license_uri = data["Members"][0]["@odata.id"]
      debug "Using Dell OEM license URI: #{license_uri}", 2
      
      # Get detailed license information
      license_response = authenticated_request(:get, license_uri)
      if license_response.status != 200
        debug "Failed to retrieve Dell OEM license details: #{license_response.status}", 1, :red
        return create_fallback_license_info(use_basic: true)
      end
      
      dell_license = JSON.parse(license_response.body)
      debug "Dell OEM license details: #{dell_license}", 2
      
      # Convert Dell OEM license format to standard format
      license_info = {
        "Id" => dell_license["EntitlementID"] || "iDRAC-License",
        "Description" => dell_license["LicenseDescription"] || "iDRAC License",
        "Name" => dell_license["LicenseDescription"] || "iDRAC License",
        "LicenseType" => dell_license["LicenseType"] || get_license_type_from_description(dell_license["LicenseDescription"]),
        "Status" => { "Health" => "OK" },
        "Removable" => true
      }
      
      return license_info
    end
    
    # Handle response from Dell Manager attributes
    def handle_dell_attributes_response(data)
      # Look for license information in attributes
      if data["Attributes"] && (
          data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] ||
          data["Attributes"]["System.ServerOS.1.OSName"] ||
          data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
        )
        
        license_info = data["Attributes"]["LicensableDevice.1.LicenseInfo.1"] || 
                       data["Attributes"]["iDRAC.Info.1.LicensingInfo"]
        
        if license_info
          license_type = license_info.include?("Enterprise") ? "Enterprise" : 
                         license_info.include?("Express") ? "Express" : "Basic"
          
          return {
            "Id" => "iDRAC-License",
            "Description" => "iDRAC8 #{license_type} License",
            "Name" => "iDRAC License",
            "LicenseType" => license_type,
            "Status" => { "Health" => "OK" },
            "Removable" => false
          }
        end
      end
      
      # If no license attributes found, fall back to feature detection
      license_type = determine_license_type()
      return {
        "Id" => "iDRAC-License",
        "Description" => "iDRAC8 #{license_type} License",
        "Name" => "iDRAC License",
        "LicenseType" => license_type,
        "Status" => { "Health" => "OK" },
        "Removable" => false
      }
    end
    
    # Handle response from Dell Manager entity
    def handle_dell_manager_response(data)
      # Look for license information in Oem data
      if data["Oem"] && data["Oem"]["Dell"]
        dell_data = data["Oem"]["Dell"]
        
        if dell_data["DellLicenseManagementService"]
          # Found license service reference, but need to query it directly
          service_uri = dell_data["DellLicenseManagementService"]["@odata.id"]
          debug "Found license service URI: #{service_uri}", 2
          
          service_response = authenticated_request(:get, service_uri)
          if service_response.status == 200
            return handle_dell_license_service_response(JSON.parse(service_response.body))
          end
        end
        
        # Check if license info is embedded directly
        if dell_data["LicenseType"] || dell_data["License"]
          license_type = dell_data["LicenseType"] || 
                        (dell_data["License"] && dell_data["License"].include?("Enterprise") ? "Enterprise" : 
                         dell_data["License"].include?("Express") ? "Express" : "Basic")
          
          return {
            "Id" => "iDRAC-License",
            "Description" => "iDRAC8 #{license_type} License",
            "Name" => "iDRAC License",
            "LicenseType" => license_type,
            "Status" => { "Health" => "OK" },
            "Removable" => false
          }
        end
      end
      
      # Check for license type in model name or description
      if data["Model"] && data["Model"].include?("Enterprise")
        return {
          "Id" => "iDRAC-License",
          "Description" => "iDRAC8 Enterprise License",
          "Name" => "iDRAC License",
          "LicenseType" => "Enterprise",
          "Status" => { "Health" => "OK" },
          "Removable" => false
        }
      end
      
      # If no license info found in manager data, fall back to feature detection
      license_type = determine_license_type()
      return {
        "Id" => "iDRAC-License",
        "Description" => "iDRAC8 #{license_type} License",
        "Name" => "iDRAC License",
        "LicenseType" => license_type,
        "Status" => { "Health" => "OK" },
        "Removable" => false
      }
    end
    
    # Get service tag from system info
    def get_service_tag
      response = authenticated_request(:get, "/redfish/v1/Systems/System.Embedded.1")
      if response.status == 200
        data = JSON.parse(response.body)
        return data["SKU"] if data["SKU"] # Service tag is usually in SKU field
      end
      
      # Try alternate location
      response = authenticated_request(:get, "/redfish/v1")
      if response.status == 200
        data = JSON.parse(response.body)
        if data["Oem"] && data["Oem"]["Dell"] && data["Oem"]["Dell"]["ServiceTag"]
          return data["Oem"]["Dell"]["ServiceTag"]
        end
      end
      
      nil
    end
    
    # Helper method to extract license type from description
    def get_license_type_from_description(description)
      return "Unknown" unless description
      
      if description.include?("Enterprise")
        return "Enterprise"
      elsif description.include?("Express")
        return "Express"
      elsif description.include?("Datacenter")
        return "Datacenter"
      else
        return "Basic"
      end
    end
    
    # Creates a basic license info object based on system information
    # Used as a fallback when neither the standard nor Dell OEM endpoints work
    # @return [Hash] A basic license info object
    def create_fallback_license_info(use_basic: false)
      # Get the iDRAC version number from server headers
      version = nil
      response = authenticated_request(:get, "/redfish/v1")
      if response.headers["server"] && response.headers["server"].match(/iDRAC\/(\d+)/i)
        version = response.headers["server"].match(/iDRAC\/(\d+)/i)[1].to_i
      end
      
      # Try to determine if it's Enterprise or Express based on available features
      license_type = use_basic ? "Basic" : determine_license_type
      
      license_info = {
        "Id" => "iDRAC-License",
        "Description" => version ? "iDRAC#{version} #{license_type} License" : "iDRAC License",
        "Name" => "iDRAC License",
        "LicenseType" => license_type,
        "Status" => { "Health" => "OK" },
        "Removable" => false
      }
      
      debug "Created fallback license info: #{license_info}", 2
      license_info
    end
    
    # Attempt to determine the license type (Enterprise/Express) based on available features
    # @return [String] The license type (Enterprise, Express, or Basic)
    def determine_license_type
      # We can try to check for features only available in Enterprise
      begin
        # For example, virtual media is typically an Enterprise feature
        virtual_media_response = authenticated_request(:get, "/redfish/v1/Managers/iDRAC.Embedded.1/VirtualMedia")
        # If we successfully get virtual media, it's likely Enterprise
        if virtual_media_response.status == 200
          return "Enterprise" 
        end
      rescue
        # If the request fails, don't fail the whole method
      end
      
      # Default to basic license if we can't determine
      return "Express"
    end
  end
end