# rubocop:disable Lint/SuppressedException
module Actions
  module Katello
    module Repository
      class Sync < Actions::EntryAction
        extend ApipieDSL::Class
        include Helpers::Presenter
        include Helpers::RollingCVRepos
        include Helpers::SmartProxySyncHelper
        include ::Actions::ObservableAction
        middleware.use Actions::Middleware::ExecuteIfContentsChanged

        execution_plan_hooks.use :notify_on_failure, :on => [:failure, :paused]

        input_format do
          param :id, Integer
          param :sync_result, Hash
        end

        # @param repo
        # @param pulp_sync_task_id in case the sync was triggered outside
        #   of Katello and we just need to finish the rest of the orchestration
        def plan(repo, options = {})
          action_subject(repo)
          repo.check_ready_to_act!

          validate_contents = options.fetch(:validate_contents, false)
          skip_metadata_check = options.fetch(:skip_metadata_check, false) || (validate_contents && (repo.yum? || repo.deb?))
          generate_applicability =  options.fetch(:generate_applicability, repo.yum? || repo.deb?)

          validate_repo!(repo: repo,
                        skip_metadata_check: skip_metadata_check,
                        skip_candlepin_check: options.fetch(:skip_candlepin_check, false))

          pulp_sync_options = {}
          pulp_sync_options[:download_policy] = ::Katello::RootRepository::DOWNLOAD_ON_DEMAND if validate_contents && repo.yum?

          #pulp3 options
          pulp_sync_options[:optimize] = false if skip_metadata_check && (repo.yum? || repo.deb?)

          sequence do
            if validate_contents
              plan_action(Katello::Repository::VerifyChecksum, repo)
            else
              sync_action = plan_action(Actions::Pulp3::Orchestration::Repository::Sync,
                                        repo,
                                        SmartProxy.pulp_primary,
                                        pulp_sync_options)
              output = sync_action.output

              update_deb_content(repo)
              plan_action(Katello::Repository::IndexContent, :id => repo.id, :force_index => skip_metadata_check)
              plan_action(Katello::Foreman::ContentUpdate, repo.environment, repo.content_view, repo)
              plan_action(Katello::Repository::FetchPxeFiles, :id => repo.id)
              concurrence do
                plan_action(Katello::Repository::ErrataMail, repo)
                plan_action(Actions::Katello::Applicability::Repository::Regenerate, :repo_ids => [repo.id]) if generate_applicability
              end
              plan_self(:id => repo.id, :sync_result => output, :skip_metadata_check => skip_metadata_check, :validate_contents => validate_contents,
                        :contents_changed => output[:contents_changed])
              plan_action(Katello::Repository::SyncHook, :id => repo.id)
            end
          end
        end

        def run
        end

        def finalize
          repo = ::Katello::Repository.find(input[:id])
          if repo
            repo&.audit_sync
            update_rolling_content_views_async(repo, input[:contents_changed])
          end
          repo.clear_smart_proxy_sync_histories if input[:contents_changed]
          schedule_async_repository_proxy_sync(repo)
        rescue ::Katello::Errors::CapsuleCannotBeReached # skip any capsules that cannot be connected to
        end

        def humanized_name
          if input && input[:validate_contents]
            _("Synchronize: Validate Content")
          elsif input && input[:skip_metadata_check]
            _("Synchronize: Skip Metadata Check")
          else
            _("Synchronize")
          end
        end

        def validate_repo!(repo:, skip_metadata_check:, skip_candlepin_check:)
          fail ::Katello::Errors::InvalidActionOptionError, _("Unable to sync repo. This repository does not have a feed url.") if repo.url.blank?
          fail ::Katello::Errors::InvalidActionOptionError, _("Cannot skip metadata check on non-yum/deb repositories.") if skip_metadata_check && !repo.yum? && !repo.deb?
          fail ::Katello::Errors::InvalidActionOptionError, _("Unable to sync repo. This repository is not a library instance repository.") unless repo.library_instance?
          ::Katello::Util::CandlepinRepositoryChecker.check_repository_for_sync!(repo) if repo.yum? && !skip_candlepin_check
        end

        def presenter
          found = all_planned_actions(Pulp3::Repository::Sync) if found.empty?
          found = all_planned_actions(Pulp3::Repository::Repair) if found.empty?
          Helpers::Presenter::Delegated.new(self, found)
        end

        def update_deb_content(repo)
          return unless repo.deb_using_structured_apt?

          plan_action(::Actions::Candlepin::Product::ContentUpdate,
                      owner:           repo.organization.label,
                      repository_id:   repo.id,
                      name:            repo.root.name,
                      type:            repo.root.content_type,
                      arches:          repo.root.format_arches,
                      label:           repo.root.custom_content_label,
                      content_url:     repo.root.custom_content_path,
                      gpg_key_url:     repo.yum_gpg_key_url,
                      metadata_expire: repo.root.metadata_expire)
        end

        def rescue_strategy
          Dynflow::Action::Rescue::Skip
        end

        def notify_on_failure(_plan)
          notification = MailNotification[:repository_sync_failure]
          repo = ::Katello::Repository.find(input.fetch(:repository, {})[:id])
          notification.users.with_enabled_email.each do |user|
            notification.deliver(user: user, repo: repo, task: task)
          end
        end

        def repository_id
          input['repository']['id']
        end

        def repository_name
          input['repository']['name']
        end

        def repository_label
          input['repository']['label']
        end

        def product_id
          input['product']['id']
        end

        def product_name
          input['product']['name']
        end

        def product_label
          input['product']['label']
        end

        def contents_changed
          input['contents_changed']
        end

        def sync_result
          input['sync_result']
        end

        apipie :class, "A class representing #{self} object" do
          desc 'This object is available as **@object** variable in
                webhook templates when a corresponding event occures.
                The following properties can be used to retrieve the needed information.'
          name "#{class_scope}"
          refs "#{class_scope}"
          sections only: %w[all webhooks]
          property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
          property :repository_id, Integer, desc: 'Returns synced repository id'
          property :repository_name, String, desc: 'Returns synced repository name'
          property :repository_label, String, desc: 'Returns synced repository label'
          property :product_id, Integer, desc: 'Returns product id the synced repository belongs to'
          property :product_name, String, desc: 'Returns product name the synced repository belongs to'
          property :product_label, String, desc: 'Returns product label the synced repository belongs to'
          property :sync_result, Hash, desc: 'Returns Hash object with sync result'
          property :contents_changed, one_of: [true, false], desc: 'Returns true if repository content was changed due to sync, false otherwise'
        end
        include Actions::Katello::JailConcern::Organization
        class Jail < ::Actions::ObservableAction::Jail
          allow :organization_id, :organization_name, :organization_label,
                :repository_id, :repository_name, :repository_label,
                :product_id, :product_name, :product_label,
                :sync_result, :contents_changed
        end
      end
    end
  end
end
