# frozen_string_literal: true

module ReleaseTools
  module InternalRelease
    class Release
      include ::SemanticLogger::Loggable

      def initialize(version:)
        @client = ReleaseTools::GitlabClient
        @version = version
        @gitlab_branch = version.stable_branch(ee: Project::GitlabEe.ee_branch?)
      end

      def execute
        logger.info('Starting internal release', version: version)

        commit = UpdateVersionFile.new(version: version, gitlab_branch: gitlab_branch).execute

        trigger_sync_to_dev

        wait_for_qa_job_on_dev(commit: commit)

        trigger_omnibus_pipeline

        trigger_cng_pipelines

        record_release_metadata

        send_slack_notification(:success, version)
      rescue StandardError => ex
        logger.fatal(error_message(ex), error: ex)

        send_slack_notification(:failed, version)

        raise
      end

      private

      attr_reader :version, :client, :gitlab_branch

      def internal_number
        version.split('-internal').last.to_i
      end

      def trigger_sync_to_dev
        ReleaseTools::InternalRelease::SyncToDevService.new.execute
      end

      def wait_for_qa_job_on_dev(commit:)
        ReleaseTools::InternalRelease::WaitDevBuildAssetsJobService
          .new(version: version, commit: commit)
          .execute
      end

      def trigger_omnibus_pipeline
        project = Project::OmnibusGitlab
        omnibus_branch = version.stable_branch

        logger.info(
          "Creating pipeline on Dev Omnibus",
          project: project,
          omnibus_branch: omnibus_branch,
          pipeline_variables: omnibus_pipeline_variables
        )

        trigger_dev_pipeline(project)
      end

      def trigger_cng_pipelines
        project = Project::CNGImage
        cng_branch = version.stable_branch

        logger.info(
          "Creating EE, UBI, and FIPS pipelines on Dev CNG/Images",
          project: project,
          cng_branch: cng_branch,
          pipeline_variables: cng_pipeline_variables
        )

        trigger_dev_pipeline(project)
        trigger_dev_pipeline(project, :ubi)
        trigger_dev_pipeline(project, :fips)
      end

      def trigger_dev_pipeline(project, type = nil)
        logger.info("Triggering pipeline for #{type} on Dev #{project}")

        return if SharedStatus.dry_run?

        pipeline_variables = if project == Project::OmnibusGitlab
                               omnibus_pipeline_variables
                             else
                               cng_pipeline_variables(type)
                             end

        Retriable.with_context(:api) do
          pipeline = ReleaseTools::GitlabDevClient.create_pipeline(
            project.dev_path,
            pipeline_variables,
            branch: version.stable_branch
          )

          logger.info(
            "#{type&.upcase} pipeline triggered on Dev #{project.dev_path}",
            pipeline: pipeline.web_url,
            pipeline_variables: pipeline_variables
          )
        end
      end

      def omnibus_pipeline_variables
        {
          ee: 'true',
          GITLAB_ASSETS_TAG: gitlab_branch,
          GITLAB_VERSION: gitlab_branch,
          INTERNAL_RELEASE: 'true',
          INTERNAL_RELEASE_ITERATION: internal_number.to_s
        }
      end

      def cng_pipeline_variables(ubi_or_fips = nil)
        {
          GITLAB_ASSETS_TAG: gitlab_branch,
          GITLAB_REF_SLUG: version,
          GITLAB_VERSION: gitlab_branch,
          INTERNAL_RELEASE_VERSION: version,
          INTERNAL_RELEASE: 'true'
        }.merge(
          case ubi_or_fips
          when :ubi
            { UBI_PIPELINE: 'true' }
          when :fips
            { FIPS_PIPELINE: 'true' }
          else
            {}
          end
        )
      end

      def record_release_metadata
        metadata = ReleaseTools::InternalRelease::Metadata.new(version: version)
        metadata.record

        logger.info('Recorded release metadata', version: version)
      end

      def send_slack_notification(status, version)
        ReleaseTools::Slack::ReleaseJobEndNotifier.new(
          job_type: "Build internal release package #{version}",
          status: status,
          release_type: :internal
        ).send_notification
      end

      def error_message(exception)
        <<~MSG
          Building the internal release package #{version} failed with error: #{exception.message}.
          Review the error log and consider retrying this job.
        MSG
      end
    end
  end
end
