# frozen_string_literal: true

module API
  module Helpers
    module Packages
      module Conan
        module ApiHelpers
          def present_download_urls(entity)
            authorize!(:read_package, project)

            presenter = ::Packages::Conan::PackagePresenter.new(recipe, current_user, project)

            render_api_error!("No recipe manifest found", 404) if yield(presenter).empty?

            present presenter, with: entity
          end

          def present_package_download_urls
            present_download_urls(EE::API::Entities::ConanPackage::ConanPackageManifest, &:package_urls)
          end

          def present_recipe_download_urls
            present_download_urls(EE::API::Entities::ConanPackage::ConanRecipeManifest, &:recipe_urls)
          end

          def recipe_upload_urls(file_names)
            { upload_urls: Hash[
              file_names.collect do |file_name|
                [file_name, recipe_file_upload_url(file_name)]
              end
            ] }
          end

          def package_upload_urls(file_names)
            { upload_urls: Hash[
              file_names.collect do |file_name|
                [file_name, package_file_upload_url(file_name)]
              end
            ] }
          end

          def package_file_upload_url(file_name)
            expose_url(
              api_v4_packages_conan_v1_files_package_path(
                package_name: params[:package_name],
                package_version: params[:package_version],
                package_username: params[:package_username],
                package_channel: params[:package_channel],
                recipe_revision: '0',
                conan_package_reference: params[:conan_package_reference],
                package_revision: '0',
                file_name: file_name
              )
            )
          end

          def recipe_file_upload_url(file_name)
            expose_url(
              api_v4_packages_conan_v1_files_export_path(
                package_name: params[:package_name],
                package_version: params[:package_version],
                package_username: params[:package_username],
                package_channel: params[:package_channel],
                recipe_revision: '0',
                file_name: file_name
              )
            )
          end

          def recipe
            "%{package_name}/%{package_version}@%{package_username}/%{package_channel}" % params.symbolize_keys
          end

          def project
            strong_memoize(:project) do
              full_path = ::Packages::ConanMetadatum.full_path_from(package_username: params[:package_username])
              Project.find_by_full_path(full_path)
            end
          end

          def package
            strong_memoize(:package) do
              project.packages
                .with_name(params[:package_name])
                .with_version(params[:package_version])
                .with_conan_channel(params[:package_channel])
                .order_created
                .last
            end
          end

          def download_package_file(file_type)
            authorize!(:read_package, project)

            package_file = ::Packages::PackageFileFinder
              .new(package, "#{params[:file_name]}", conan_file_type: file_type).execute!

            track_event('pull_package') if params[:file_name] == ::Packages::ConanFileMetadatum::PACKAGE_BINARY

            present_carrierwave_file!(package_file.file)
          end

          def find_or_create_package
            package || ::Packages::Conan::CreatePackageService.new(project, current_user, params).execute
          end

          def track_push_package_event
            if params[:file_name] == ::Packages::ConanFileMetadatum::PACKAGE_BINARY && params['file.size'].positive?
              track_event('push_package')
            end
          end

          def create_package_file_with_type(file_type, current_package)
            unless params['file.size'] == 0
              # conan sends two upload requests, the first has no file, so we skip record creation if file.size == 0
              ::Packages::Conan::CreatePackageFileService.new(current_package, uploaded_package_file, params.merge(conan_file_type: file_type)).execute
            end
          end

          def upload_package_file(file_type)
            authorize_upload!(project)

            current_package = find_or_create_package

            track_push_package_event

            create_package_file_with_type(file_type, current_package)
          rescue ObjectStorage::RemoteStoreError => e
            Gitlab::ErrorTracking.track_exception(e, file_name: params[:file_name], project_id: project.id)

            forbidden!
          end

          def find_personal_access_token
            personal_access_token = find_personal_access_token_from_conan_jwt ||
              find_personal_access_token_from_http_basic_auth

            personal_access_token
          end

          def find_user_from_job_token
            return unless route_authentication_setting[:job_token_allowed]

            job = find_job_from_token || raise(::Gitlab::Auth::UnauthorizedError)

            job.user
          end

          def find_job_from_token
            find_job_from_conan_jwt || find_job_from_http_basic_auth
          end

          # We need to override this one because it
          # looks into Bearer authorization header
          def find_oauth_access_token
          end

          def find_personal_access_token_from_conan_jwt
            token = decode_oauth_token_from_jwt

            return unless token

            PersonalAccessToken.find_by_id_and_user_id(token.access_token_id, token.user_id)
          end

          def find_job_from_conan_jwt
            token = decode_oauth_token_from_jwt

            return unless token

            ::Ci::Build.find_by_token(token.access_token_id.to_s)
          end

          def decode_oauth_token_from_jwt
            jwt = Doorkeeper::OAuth::Token.from_bearer_authorization(current_request)

            return unless jwt

            token = ::Gitlab::ConanToken.decode(jwt)

            return unless token && token.access_token_id && token.user_id

            token
          end
        end
      end
    end
  end
end
