# frozen_string_literal: true

module ReleaseTools
  module Services
    class AutoDeployBranchService
      include ::SemanticLogger::Loggable
      include BranchCreation

      CI_VAR_AUTO_DEPLOY = 'AUTO_DEPLOY_BRANCH'
      COMMIT_AGE_THRESHOLD = 5.days
      PROJECTS = [Project::GitlabEe, Project::OmnibusGitlab, Project::CNGImage].freeze

      Result = Struct.new(:project, :branch, :response, :notify_branch_too_far_behind)

      attr_reader :branch_name

      def initialize(branch_name)
        @branch_name = branch_name
      end

      def create_branches!
        if all_refs_equal_to_limit_shas?
          logger.warn('Not creating auto deploy branches because there are no new commits with passing builds')
          return []
        end

        results = PROJECTS.map do |project|
          # Find passing commits before creating branch
          ref = latest_successful_ref(project)

          branch = create_branch_from_ref(project.auto_deploy_path, branch_name, ref)

          Result.new(project.auto_deploy_path, branch_name, branch, branch_too_far_behind?(project))
        end

        update_auto_deploy_ci

        results
      end

      private

      def latest_successful_ref(project)
        passing_build(project).for_auto_deploy_branch.id
      end

      def previous_package_sha(project)
        passing_build(project).previous_package_sha
      end

      def passing_build(project)
        @passing_build ||= Hash.new do |hash, proj|
          hash[proj] = PassingBuild.new(proj.default_branch, proj)
        end

        @passing_build[project]
      end

      def all_refs_equal_to_limit_shas?
        refs = PROJECTS.map { |project| latest_successful_ref(project) }
        limit_shas = PROJECTS.map { |project| previous_package_sha(project) }

        PROJECTS.each_with_index.all? do |project, index|
          equal = refs[index] == limit_shas[index]

          logger.warn('New auto deploy branch will have same SHA as previous package', project: project, sha: refs[index]) if equal

          equal
        end
      end

      # Returns true if commits excluded from the auto deploy branch are 5 days old or more.
      # We're checking the "age" of the excluded commit (with the committed_date as the birthday).
      # If the age is 5 days or more, return true. Since we usually deploy multiple times a day,
      # a commit should not reach the age of 5 days without being included in an auto deploy branch.
      def branch_too_far_behind?(project)
        excluded_commit = passing_build(project).next_commit
        return false unless excluded_commit

        commit_age = Time.current - Time.iso8601(excluded_commit.committed_date)

        commit_age >= COMMIT_AGE_THRESHOLD
      end

      def update_auto_deploy_ci
        return if SharedStatus.dry_run?

        gitlab_ops_client = ReleaseTools::GitlabOpsClient

        begin
          gitlab_ops_client.update_variable('gitlab-org/release/tools', CI_VAR_AUTO_DEPLOY, branch_name)
        rescue Gitlab::Error::NotFound
          gitlab_ops_client.create_variable('gitlab-org/release/tools', CI_VAR_AUTO_DEPLOY, branch_name)
        end
      end
    end
  end
end
