# frozen_string_literal: true

module Types
  class VulnerabilityType < BaseObject
    graphql_name 'Vulnerability'
    description 'Represents a vulnerability'

    implements(Types::Notes::NoteableType)

    authorize :read_vulnerability

    expose_permissions Types::PermissionTypes::Vulnerability

    field :id, GraphQL::ID_TYPE, null: false,
          description: 'GraphQL ID of the vulnerability.'

    field :title, GraphQL::STRING_TYPE, null: true,
          description: 'Title of the vulnerability.'

    field :description, GraphQL::STRING_TYPE, null: true,
          description: 'Description of the vulnerability.'

    field :state, VulnerabilityStateEnum, null: true,
          description: "State of the vulnerability (#{::Vulnerability.states.keys.join(', ').upcase})"

    field :severity, VulnerabilitySeverityEnum, null: true,
          description: "Severity of the vulnerability (#{::Enums::Vulnerability.severity_levels.keys.join(', ').upcase})"

    field :report_type, VulnerabilityReportTypeEnum, null: true,
          description: "Type of the security report that found the vulnerability (#{::Enums::Vulnerability.report_types.keys.join(', ').upcase})"

    field :resolved_on_default_branch, GraphQL::BOOLEAN_TYPE, null: false,
          description: "Indicates whether the vulnerability is fixed on the default branch or not."

    field :user_notes_count, GraphQL::INT_TYPE, null: false,
          description: 'Number of user notes attached to the vulnerability.'

    field :vulnerability_path, GraphQL::STRING_TYPE, null: true,
          description: "URL to the vulnerability's details page."

    field :issue_links, ::Types::Vulnerability::IssueLinkType.connection_type, null: false,
          description: "List of issue links related to the vulnerability.",
          resolver: Resolvers::Vulnerabilities::IssueLinksResolver

    field :external_issue_links, ::Types::Vulnerability::ExternalIssueLinkType.connection_type, null: false,
          description: 'List of external issue links related to the vulnerability.'

    field :location, VulnerabilityLocationType, null: true,
          description: 'Location metadata for the vulnerability. Its fields depend on the type of security scan that found the vulnerability.'

    field :scanner, VulnerabilityScannerType, null: true,
          description: 'Scanner metadata for the vulnerability.'

    field :primary_identifier, VulnerabilityIdentifierType, null: true,
          description: 'Primary identifier of the vulnerability.'

    field :identifiers, [VulnerabilityIdentifierType], null: false,
          description: 'Identifiers of the vulnerability.'

    field :project, ::Types::ProjectType, null: true,
          description: 'The project on which the vulnerability was found.',
          authorize: :read_project

    field :detected_at, Types::TimeType, null: false,
          description: 'Timestamp of when the vulnerability was first detected.',
          method: :created_at

    field :confirmed_at, Types::TimeType, null: true,
          description: 'Timestamp of when the vulnerability state was changed to confirmed.'

    field :resolved_at, Types::TimeType, null: true,
          description: 'Timestamp of when the vulnerability state was changed to resolved.'

    field :dismissed_at, Types::TimeType, null: true,
          description: 'Timestamp of when the vulnerability state was changed to dismissed.'

    field :has_solutions, GraphQL::BOOLEAN_TYPE, null: true,
          description: 'Indicates whether there is a solution available for this vulnerability.',
          resolver_method: :has_solutions?

    field :merge_request, ::Types::MergeRequestType, null: true,
          description: 'Merge request that fixes the vulnerability.'

    field :confirmed_by, ::Types::UserType, null: true,
          description: 'The user that confirmed the vulnerability.'

    field :resolved_by, ::Types::UserType, null: true,
          description: 'The user that resolved the vulnerability.'

    field :dismissed_by, ::Types::UserType, null: true,
          description: 'The user that dismissed the vulnerability.'

    field :details, [VulnerabilityDetailType], null: false,
          description: 'Details of the vulnerability.',
          resolver: Resolvers::Vulnerabilities::DetailsResolver

    def confirmed_by
      ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::User, object.confirmed_by_id).find
    end

    def resolved_by
      ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::User, object.resolved_by_id).find
    end

    def dismissed_by
      ::Gitlab::Graphql::Loaders::BatchModelLoader.new(::User, object.dismissed_by_id).find
    end

    def user_notes_count
      ::Gitlab::Graphql::Aggregations::Vulnerabilities::LazyUserNotesCountAggregate.new(context, object)
    end

    def vulnerability_path
      ::Gitlab::Routing.url_helpers.project_security_vulnerability_path(object.project, object)
    end

    def location
      object_location = object.finding&.location
      object_location&.merge(blob_path: object.blob_path, report_type: object.report_type)&.compact
    end

    def scanner
      Representation::VulnerabilityScannerEntry.new(object.finding&.scanner, object.report_type)
    end

    def primary_identifier
      object.finding&.primary_identifier
    end

    def identifiers
      object.finding&.identifiers
    end

    def description
      object.description || object.finding_description
    end

    def project
      Gitlab::Graphql::Loaders::BatchModelLoader.new(Project, object.project_id).find
    end

    def merge_request
      object.finding&.merge_request_feedback&.merge_request
    end

    def has_solutions?
      object.finding&.remediations&.any?
    end
  end
end
