# frozen_string_literal: true

module API
  class VulnerabilityFindings < ::API::Base
    include PaginationParams
    include ::Gitlab::Utils::StrongMemoize

    feature_category :vulnerability_management

    helpers do
      def pipeline
        strong_memoize(:pipeline) do
          if params[:pipeline_id]
            user_project.all_pipelines.for_id(params[:pipeline_id]).first
          else
            user_project.latest_pipeline_with_security_reports
          end
        end
      end

      def vulnerability_findings
        @vulnerability_findings ||= begin
          return paginate(Kaminari.paginate_array([])) unless pipeline

          with_adaptive_finder || with_vulnerabilities_finder
        end
      end

      def with_vulnerabilities_finder
        aggregated_report = Security::PipelineVulnerabilitiesFinder.new(pipeline: pipeline, params: declared_params).execute

        # We might have to add rubocop:disable annotation here in case
        # https://gitlab.com/gitlab-org/gitlab/issues/32763 happens, using
        # Kaminari.paginate_array here is correct
        # See https://gitlab.com/gitlab-org/gitlab/issues/33588#note_291849433
        # for discussion
        paginate(Kaminari.paginate_array(aggregated_report.findings))
      end

      def with_adaptive_finder
        result = Security::FindingsFinder.new(pipeline, params: declared_params).execute
        return unless result

        paginate(result).findings
      end
    end

    before do
      authenticate!
    end

    params do
      requires :id, type: String, desc: 'The ID of a project'
    end
    resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
      params do
        optional :report_type, type: Array[String],
                 coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
                 desc: 'The type of report vulnerability belongs to',
                 values: ::Vulnerabilities::Finding.report_types.keys,
                 default: ::Vulnerabilities::Finding.report_types.keys
        optional :scope, type: String, desc: 'Return vulnerabilities for the given scope: `dismissed` or `all`',
                 default: 'dismissed', values: %w[all dismissed]
        optional :severity,
                 type: Array[String],
                 coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
                 desc: 'Returns vulnerabilities belonging to specified severity level: '\
                       '`info`, `unknown`, `low`, `medium`, `high`, or `critical`. Defaults to all',
                 values: ::Vulnerabilities::Finding.severities.keys,
                 default: ::Vulnerabilities::Finding.severities.keys
        optional :confidence,
                 type: Array[String],
                 coerce_with: ::API::Validations::Types::CommaSeparatedToArray.coerce,
                 desc: 'Returns vulnerabilities belonging to specified confidence level: '\
                       '`ignore`, `unknown`, `experimental`, `low`, `medium`, `high`, or `confirmed`. '\
                       'Defaults to all',
                 values: ::Vulnerabilities::Finding.confidences.keys,
                 default: ::Vulnerabilities::Finding.confidences.keys
        optional :pipeline_id, type: String, desc: 'The ID of the pipeline'

        use :pagination
      end
      desc 'Get a list of project vulnerability findings' do
        success ::Vulnerabilities::FindingEntity
      end
      get ':id/vulnerability_findings' do
        authorize! :read_vulnerability, user_project

        Gitlab::Vulnerabilities::FindingsPreloader.preload_feedback!(vulnerability_findings)

        present vulnerability_findings,
                with: ::Vulnerabilities::FindingEntity,
                request: GrapeRequestProxy.new(request, current_user)
      end
    end
  end
end
