# frozen_string_literal: true

module BingAdsRubySdk
  class AugmentedParser
    def initialize(wsdl_file_path)
      @lolsoap_parser = LolSoap::WSDLParser.parse(File.read(wsdl_file_path))
      @concrete_abstract_mapping = {}
    end

    def call
      add_subtypes_to_definitions

      [lolsoap_parser, concrete_abstract_mapping]
    end

    private

    attr_reader :lolsoap_parser, :concrete_abstract_mapping

    # adds subtypes to existing definitions.
    # for instance, the wsdl specifies AdExtensionAssociation are accepted for AddAdExtension
    # but there is no way to specify the type we want
    # the goal is to:
    # - validate properly the attributes
    # - ensure the attributes are properly formatted when xml is created
    # - ensure we inject proper type to the xml
    def add_subtypes_to_definitions
      # to augment all types definitions
      lolsoap_parser.types.each_value do |content|
        add_subtypes(content[:elements])
      end
      # we have to augment operations because some Requests are abstract, for instance:
      # ReportRequest which can be AccountPerformanceReportRequest etc...
      lolsoap_parser.operations.each_value do |content|
        content[:input][:body].each do |full_name|
          add_subtypes(lolsoap_parser.elements[full_name][:type][:elements])
        end
      end
      @grouped_subtypes = nil # we can reset this as its not needed anymore
    end

    def add_subtypes(content)
      content.keys.each do |base|
        grouped_subtypes.fetch(base, []).each do |sub_type|
          elem = lolsoap_parser.elements[sub_type.id]
          elem[:base_type_name] = base
          content[sub_type.name] = elem
        end
      end
    end

    def grouped_subtypes
      @grouped_subtypes ||= begin
        grouped_types = {}
        # types are defined there: https://github.com/loco2/lolsoap/blob/master/lib/lolsoap/wsdl_parser.rb#L305
        lolsoap_parser.each_node('xs:complexType[not(@abstract="true")]') do |node, schema|
          type = ::LolSoap::WSDLParser::Type.new(lolsoap_parser, schema, node)
          if type.base_type # it has a base_type, its a subtype
            base_type = extract_base_type(type.base_type)
            concrete_abstract_mapping[type.name] = base_type.name
            grouped_types[base_type.name] ||= []
            grouped_types[base_type.name].push(type)
          end
        end
        grouped_types
      end
    end

    # we want the real base: sometimes there are many layers of inheritance
    def extract_base_type(type)
      if type.base_type
        extract_base_type(type.base_type)
      else
        type
      end
    end
  end
end