# frozen_string_literal: true

module ReleaseTools
  module Services
    module OmnibusPackages
      class Base
        include ::SemanticLogger::Loggable

        OmnibusPackagesError = Class.new(StandardError)
        OmnibusPackagesInProgressError = Class.new(StandardError)

        FAILED_STATUSES = %w[failed canceled skipped manual].freeze
        SUCCESS_STATUS = "success"

        def initialize(version:, package_type: :tag)
          @version = version
          @packages_statuses = {}
          @package_type = package_type
        end

        def execute
          logger.info("Verifying Omnibus CE/EE package #{action_progressive_tense} for #{version}")

          Retriable.with_context(:release_package_pipeline, on: OmnibusPackagesInProgressError) do
            @packages_statuses = packages_service.new(version, package_type: package_type).execute

            if packages_statuses.empty?
              raise OmnibusPackagesError, "No Omnibus packages found for #{version}"
            end

            if all_packages_succeeded?
              log_success
              send_slack_notification(:success)
            elsif any_package_failed?
              log_failure
              raise OmnibusPackagesError
            else
              log_in_progress
              raise OmnibusPackagesInProgressError
            end
          end
        rescue StandardError
          send_slack_notification(:failed)
          raise
        end

        private

        attr_accessor :packages_statuses
        attr_reader :version, :package_type

        def action_progressive_tense
          raise NotImplementedError, "Subclasses must implement action_progressive_tense"
        end

        def packages_service
          raise NotImplementedError, "Subclasses must implement packages_service"
        end

        def all_packages_succeeded?
          packages_statuses.values.all?(SUCCESS_STATUS)
        end

        def any_package_failed?
          packages_statuses.values.any? { |status| FAILED_STATUSES.include?(status) }
        end

        def log_success
          logger.info("Packages for #{packages_statuses.keys.join(', ')} have completed #{action_progressive_tense}")
        end

        def log_failure
          failed_packages = packages_statuses.select { |_, status| FAILED_STATUSES.include?(status) }
          logger.error("Failed to complete #{action_progressive_tense} packages: #{failed_packages}")
        end

        def log_in_progress
          in_progress = packages_statuses.reject { |_, status| FAILED_STATUSES.include?(status) || status == SUCCESS_STATUS }
          logger.info("Package #{action_progressive_tense} is in progress: #{in_progress}")
        end

        def send_slack_notification(status)
          job_type = if internal?
                       "Verify internal package is built and available for version #{version}"
                     else
                       "Verify package #{action_progressive_tense} for version #{version}"
                     end

          ReleaseTools::Slack::ReleaseJobEndNotifier
            .new(job_type: job_type, status: status, release_type: release_type)
            .send_notification
        end

        def release_type
          if internal?
            :internal
          elsif patch?
            :patch
          else
            :monthly
          end
        end

        def patch?
          ReleaseTools::Version.new(version).patch?
        end

        def internal?
          package_type == :internal
        end
      end
    end
  end
end
