# frozen_string_literal: true

module ReleaseTools
  module Security
    class TargetIssuesProcessor
      include ::SemanticLogger::Loggable
      TRACKING_ISSUE_PROJECT = 'gitlab-org/gitlab'

      def execute
        if security_target_issues.empty?
          logger.info("No target issues found, skipping.")

          return
        end

        logger.info("#{security_target_issues.count} target issues found. They will be evaluated and considered for linking to the patch release tracking issue: #{security_release_tracking_issue.web_url}.")

        security_target_issues.each do |target_issue|
          case [target_issue.ready_to_be_processed?, linked_to_security_tracking_issue?(target_issue)]
          when [true, true]
            logger.info("#{target_issue.web_url} is already linked to the patch release tracking issue and still ready to be processed.")

            nil
          when [false, false]
            logger.info("#{target_issue.web_url} is not ready to be processed or linked to the patch release tracking issue.")

            TargetIssueNotifier.notify(:not_ready, target_issue)
            remove_security_target_label(target_issue)
          when [true, false]
            logger.info("#{target_issue.web_url} is ready to be processed and will be linked to the patch release tracking issue.")

            link(target_issue)
            TargetIssueNotifier.notify(:linked, target_issue)
            SecurityReleaseTrackingIssueNotifier.notify(target_issue)
            ImplementationIssueProcessor.new(target_issue).execute
          when [false, true]
            logger.info("#{target_issue.web_url} will be unlinked from  the patch release tracking issue as it is no longer ready to be processed.")

            unlink(target_issue)
            TargetIssueNotifier.notify(:unlinked, target_issue)
            remove_security_target_label(target_issue)
          end
        end

        update_security_issue_table
      end

      private

      def link(issue)
        return if SharedStatus.dry_run?

        Retriable.with_context(:api) do
          client.link_issues(security_release_tracking_issue, issue)
        end
      end

      def unlink(issue)
        return if SharedStatus.dry_run?

        Retriable.with_context(:api) do
          client.delete_issue_link(
            security_release_tracking_issue,
            link_id_for(issue)
          )
        end
      end

      def link_id_for(issue)
        tracking_issue_links.find { |link| link.id == issue.issue.id }.issue_link_id
      end

      def linked_to_security_tracking_issue?(issue)
        linked_security_issues.any? do |linked_issue|
          linked_issue.iid == issue.iid && linked_issue.project_id == issue.project_id
        end
      end

      def tracking_issue_links
        @tracking_issue_links ||= Retriable.with_context(:api) do
          client.issue_links(TRACKING_ISSUE_PROJECT, security_release_tracking_issue.iid)
        end
      end

      def security_release_tracking_issue
        @security_release_tracking_issue ||= ReleaseTools::Issue.new(issue_crawler.security_tracking_issue)
      end

      def linked_security_issues
        @linked_security_issues ||= issue_crawler.related_security_issues
      end

      def security_target_issues
        @security_target_issues ||= issue_crawler.evaluable_security_issues
      end

      def issue_crawler
        @issue_crawler ||= Security::IssueCrawler.new
      end

      def remove_security_target_label(issue)
        return if SharedStatus.dry_run?

        issue_data = issue.issue
        issue_data.labels.delete('security-target')

        Retriable.with_context(:api) do
          client.update_issue(issue_data, issue.project)
        end
      end

      def update_security_issue_table
        ReleaseTools::Security::IssueTable::Service.new(client).execute
      end

      def client
        ReleaseTools::GitlabClient
      end
    end
  end
end
