# frozen_string_literal: true

require "resolv"
require "uri"

module Kenna
  module 128iid
    module Edgescan
      class EdgescanVulnerability
        attr_accessor :asset, :data, :host

        def initialize(asset, vulnerability, host)
          @asset = asset
          @data = vulnerability
          @host = host
        end

        def id
          data["id"]
        end

        def name
          data["name"]
        end

        def location
          data["location"]
        end

        def location_specifier_id
          data["location_specifier_id"]
        end

        def location_specifiers
          asset["location_specifiers"]
        end

        def matching_location_specifier
          location_specifiers.find { |specifier| not_ip_range?(specifier) && specifier["id"] == location_specifier_id } ||
            location_specifiers.find { |specifier| not_ip_range?(specifier) && location.include?(specifier["location"]) }
        end

        def to_kenna_vulnerability
          {
            "scanner_identifier" => data["definition_id"],
            "vuln_def_name" => name,
            "external_unique_id" => id,
            "scanner_type" => scanner_type,
            "created_at" => data["created_at"],
            "last_seen_at" => data["updated_at"],
            "status" => data["status"],
            "details" => details,
            "scanner_score" => data["threat"] * 2
          }
        end

        def to_kenna_finding
          {
            "scanner_identifier" => data["definition_id"],
            "vuln_def_name" => name,
            "external_unique_id" => id,
            "scanner_type" => scanner_type,
            "created_at" => data["created_at"],
            "last_seen_at" => data["updated_at"],
            "severity" => data["threat"] * 2,
            "additional_fields" => {
              "status" => data["status"],
              "details" => details
            }
          }
        end

        def to_kenna_asset
          if url?
            { **kenna_asset_base, **{ "url" => location }, **host_information }
          elsif ip_address?
            { **kenna_asset_base, **{ "ip_address" => location }, **host_information }
          else
            { **kenna_asset_base, **{ "hostname" => location }, **host_information }
          end
        end

        def external_id
          if host
            "ES#{asset['id']} #{host['location']}"
          elsif matching_location_specifier
            "ES#{asset['id']} #{matching_location_specifier['location']}"
          else
            "ES#{asset['id']} #{location}"
          end
        end

        private

        def kenna_asset_base
          {
            "external_id" => external_id,
            "tags" => asset["tags"],
            "application" => asset_application_id
          }
        end

        def host_information
          return {} unless host

          {
            "external_id" => "ES#{asset['id']} #{host['location']}",
            "ip_address" => host["location"],
            "hostname" => host["hostnames"]&.first,
            "os_version" => host["os_name"]
          }
        end

        def scanner_type
          data["layer"] == "application" ? "EdgescanApp" : "EdgescanNet"
        end

        def details
          "Location: #{location}\n" + data["details"].map { |detail| "#{detail_title(detail)}#{detail['src']}" }
                                                     .join("\n\n====================\n\n")
        end

        def detail_title(detail)
          type = detail["type"]
          return "#{detail['protocol']}:#{detail['port']}\n\n" if type == "service"
          return "Vulnerable #{detail['parameter_type']}: #{detail['parameter_name']}\n\n" if type == "attack_vector"

          ""
        end

        def ip_address?
          !!(location =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex]))
        end

        def url?
          !!(location =~ URI::DEFAULT_PARSER.make_regexp)
        end

        def not_ip_range?(specifier)
          !["cidr", "block"].include?(specifier["location_type"])
        end

        def asset_application_id
          "#{asset['name']} (ES#{asset['id']})" if asset_application? && url?
        end

        def asset_application?
          asset["type"] == "app"
        end
      end
    end
  end
end
