require_relative '../happy_mapper_tools/stig_attributes'
require_relative '../happy_mapper_tools/cci_attributes'
require_relative '../utilities/inspec_util'

require 'digest'
require 'json'

module InspecTools
  class XCCDF
    def initialize(xccdf, use_vuln_id, replace_tags = nil)
      @xccdf = xccdf
      @xccdf = replace_tags_in_xccdf(replace_tags, @xccdf) unless replace_tags.nil?
      cci_list_path = File.join(File.dirname(__FILE__), '../data/U_CCI_List.xml')
      @cci_items = HappyMapperTools::CCIAttributes::CCI_List.parse(File.read(cci_list_path))
      register_after_parse_callbacks
      @benchmark = HappyMapperTools::StigAttributes::Benchmark.parse(@xccdf)
      @use_vuln_id = use_vuln_id
    end

    def to_ckl
      # TODO: to_ckl
    end

    def to_csv
      # TODO: to_csv
    end

    def to_inspec
      @profile = {}
      @controls = []
      insert_json_metadata
      insert_controls
      @profile['sha256'] = Digest::SHA256.hexdigest @profile.to_s
      @profile
    end

    ####
    # extracts non-InSpec attributes
    ###
    # TODO there may be more attributes we want to extract, see data/attributes.yml for example
    def to_attributes
      @attribute = {}

      @attribute['benchmark.title'] = @benchmark.title
      @attribute['benchmark.id'] = @benchmark.id
      @attribute['benchmark.description'] = @benchmark.description
      @attribute['benchmark.version'] = @benchmark.version

      @attribute['benchmark.status'] = @benchmark.status
      @attribute['benchmark.status.date'] = @benchmark.release_date.release_date

      @attribute['benchmark.notice.id'] = @benchmark.notice.id

      @attribute['benchmark.plaintext'] = @benchmark.plaintext.plaintext
      @attribute['benchmark.plaintext.id'] = @benchmark.plaintext.id

      @attribute['reference.href'] = @benchmark.reference.href
      @attribute['reference.dc.publisher'] = @benchmark.reference.dc_publisher
      @attribute['reference.dc.source'] = @benchmark.reference.dc_source
      @attribute['reference.dc.title'] = @benchmark.group[0].rule.reference.dc_title if !@benchmark.group[0].nil?
      @attribute['reference.dc.subject'] = @benchmark.group[0].rule.reference.dc_subject if !@benchmark.group[0].nil?
      @attribute['reference.dc.type'] = @benchmark.group[0].rule.reference.dc_type if !@benchmark.group[0].nil?
      @attribute['reference.dc.identifier'] = @benchmark.group[0].rule.reference.dc_identifier if !@benchmark.group[0].nil?

      @attribute['content_ref.name'] = @benchmark.group[0].rule.check.content_ref.name if !@benchmark.group[0].nil?
      @attribute['content_ref.href'] = @benchmark.group[0].rule.check.content_ref.href if !@benchmark.group[0].nil?

      @attribute
    end

    def publisher
      @benchmark.reference.dc_publisher
    end

    def published
      @benchmark.release_date.release_date
    end

    def inject_metadata(metadata = '{}')
      json_metadata = JSON.parse(metadata)
      json_metadata.each do |key, value|
        @profile[key] = value
      end
    end

    private

    def register_after_parse_callbacks
      # Determine if the parsed Ident is refrencing a legacy ID number.
      HappyMapperTools::StigAttributes::Ident.after_parse do |object|
        object.cci = object.system.eql?('http://cyber.mil/cci')
        object.legacy = !object.cci
      end
    end

    def replace_tags_in_xccdf(replace_tags, xccdf_xml)
      replace_tags.each do |tag|
        xccdf_xml = xccdf_xml.gsub(/(&lt;|<)#{tag}(&gt;|>)/, "$#{tag}")
      end
      xccdf_xml
    end

    def insert_json_metadata
      @profile['name'] = @benchmark.id
      @profile['title'] = @benchmark.title
      @profile['maintainer'] = 'The Authors' if @profile['maintainer'].nil?
      @profile['copyright'] = 'The Authors' if @profile['copyright'].nil?
      @profile['copyright_email'] = 'you@example.com' if @profile['copyright_email'].nil?
      @profile['license'] = 'Apache-2.0' if @profile['license'].nil?
      @profile['summary'] = "\"#{@benchmark.description.gsub('\\', '\\\\\\').gsub('"', '\"')}\""
      @profile['version'] = '0.1.0' if @profile['version'].nil?
      @profile['supports'] = []
      @profile['attributes'] = []
      @profile['generator'] = {
        name: 'inspec_tools',
        version: VERSION
      }
      @profile['plaintext'] = @benchmark.plaintext.plaintext
      @profile['status'] = "#{@benchmark.status} on #{@benchmark.release_date.release_date}"
      @profile['reference_href'] = @benchmark.reference.href
      @profile['reference_publisher'] = @benchmark.reference.dc_publisher
      @profile['reference_source'] = @benchmark.reference.dc_source
    end

    def insert_controls
      @benchmark.group.each do |group|
        control = {}
        control['id'] = @use_vuln_id ? group.id : group.rule.id.split('r').first
        control['title'] = group.rule.title
        control['desc'] = group.rule.description.vuln_discussion.split('Satisfies: ')[0]
        control['impact'] = Utils::InspecUtil.get_impact(group.rule.severity)
        control['tags'] = {}
        control['tags']['severity'] = Utils::InspecUtil.get_impact_string(control['impact'])
        control['tags']['gtitle'] = group.title
        control['tags']['satisfies'] = group.rule.description.vuln_discussion.split('Satisfies: ')[1].split(',').map(&:strip) if group.rule.description.vuln_discussion.split('Satisfies: ').length > 1
        control['tags']['gid'] = group.id
        control['tags']['rid'] = group.rule.id
        control['tags']['stig_id'] = group.rule.version
        control['tags']['fix_id'] = group.rule.fix.id
        control['tags']['cci'] = group.rule.idents.select(&:cci).map(&:ident)
        legacy_tags = group.rule.idents.select(&:legacy)
        control['tags']['legacy'] = legacy_tags.map(&:ident) unless legacy_tags.empty?
        control['tags']['nist'] = @cci_items.fetch_nists(control['tags']['cci'])
        control['tags']['false_negatives'] = group.rule.description.false_negatives if group.rule.description.false_negatives != ''
        control['tags']['false_positives'] = group.rule.description.false_positives if group.rule.description.false_positives != ''
        control['tags']['documentable'] = group.rule.description.documentable if group.rule.description.documentable != ''
        control['tags']['mitigations'] = group.rule.description.false_negatives if group.rule.description.mitigations != ''
        control['tags']['severity_override_guidance'] = group.rule.description.severity_override_guidance if group.rule.description.severity_override_guidance != ''
        control['tags']['security_override_guidance'] = group.rule.description.security_override_guidance if group.rule.description.security_override_guidance != ''
        control['tags']['potential_impacts'] = group.rule.description.potential_impacts if group.rule.description.potential_impacts != ''
        control['tags']['third_party_tools'] = group.rule.description.third_party_tools if group.rule.description.third_party_tools != ''
        control['tags']['mitigation_controls'] = group.rule.description.mitigation_controls if group.rule.description.mitigation_controls != ''
        control['tags']['responsibility'] = group.rule.description.responsibility if group.rule.description.responsibility != ''
        control['tags']['ia_controls'] = group.rule.description.ia_controls if group.rule.description.ia_controls != ''
        control['tags']['check'] = group.rule.check.content
        control['tags']['fix'] = group.rule.fixtext
        @controls << control
      end
      @profile['controls'] = @controls
    end
  end
end
