# frozen_string_literal: true

module Integrations
  module ChatMessage
    class PipelineMessage < BaseMessage
      MAX_VISIBLE_JOBS = 10

      attr_reader :user
      attr_reader :ref_type
      attr_reader :ref
      attr_reader :status
      attr_reader :detailed_status
      attr_reader :duration
      attr_reader :finished_at
      attr_reader :pipeline_id
      attr_reader :failed_stages
      attr_reader :failed_jobs

      attr_reader :project
      attr_reader :commit
      attr_reader :committer
      attr_reader :pipeline

      def initialize(data)
        super

        @user = data[:user]
        @user_name = data.dig(:user, :username) || 'API'

        pipeline_attributes = data[:object_attributes]
        @ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
        @ref = pipeline_attributes[:ref]
        @status = pipeline_attributes[:status]
        @detailed_status = pipeline_attributes[:detailed_status]
        @duration = pipeline_attributes[:duration].to_i
        @finished_at = pipeline_attributes[:finished_at] ? Time.parse(pipeline_attributes[:finished_at]).to_i : nil
        @pipeline_id = pipeline_attributes[:id]

        # Get list of jobs that have actually failed (after exhausting all retries)
        @failed_jobs = actually_failed_jobs(Array(data[:builds]))
        @failed_stages = @failed_jobs.map { |j| j[:stage] }.uniq

        @project = Project.find(data[:project][:id])
        @commit = project.commit_by(oid: data[:commit][:id])
        @committer = commit.committer
        @pipeline = Ci::Pipeline.find(pipeline_id)
      end

      def pretext
        ''
      end

      def attachments
        return message if markdown

        [{
          fallback: format(message),
          color: attachment_color,
          author_name: strip_markup(user_combined_name),
          author_icon: user_avatar,
          author_link: author_url,
          title: s_("ChatMessage|Pipeline #%{pipeline_id} %{humanized_status} in %{duration}") %
            {
              pipeline_id: pipeline_id,
              humanized_status: humanized_status,
              duration: pretty_duration(duration)
            },
          title_link: pipeline_url,
          fields: attachments_fields,
          footer: project.name,
          footer_icon: project.avatar_url(only_path: false),
          ts: finished_at
        }]
      end

      def activity
        {
          title: s_("ChatMessage|Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status}") %
            {
              pipeline_link: pipeline_link,
              ref_type: ref_type,
              ref_link: ref_link,
              user_combined_name: strip_markup(user_combined_name),
              humanized_status: humanized_status
            },
          subtitle: s_("ChatMessage|in %{project_link}") % { project_link: project_link },
          text: s_("ChatMessage|in %{duration}") % { duration: pretty_duration(duration) },
          image: user_avatar || ''
        }
      end

      def attachment_color
        case status
        when 'success'
          detailed_status == 'passed with warnings' ? 'warning' : 'good'
        else
          'danger'
        end
      end

      private

      def actually_failed_jobs(builds)
        succeeded_job_names = builds.map { |b| b[:name] if b[:status] == 'success' }.compact.uniq

        failed_jobs = builds.select do |build|
          # Select jobs which doesn't have a successful retry
          build[:status] == 'failed' && succeeded_job_names.exclude?(build[:name])
        end

        failed_jobs.uniq { |job| job[:name] }.reverse
      end

      def failed_stages_field
        {
          title: s_("ChatMessage|Failed stage").pluralize(failed_stages.length),
          value: ::Slack::Messenger::Util::LinkFormatter.format(failed_stages_links),
          short: true
        }
      end

      def failed_jobs_field
        {
          title: s_("ChatMessage|Failed job").pluralize(failed_jobs.length),
          value: ::Slack::Messenger::Util::LinkFormatter.format(failed_jobs_links),
          short: true
        }
      end

      def yaml_error_field
        {
          title: s_("ChatMessage|Invalid CI config YAML file"),
          value: pipeline.error_messages,
          short: false
        }
      end

      def pipeline_name_field
        {
          title: s_("ChatMessage|Pipeline name"),
          value: pipeline.name,
          short: false
        }
      end

      def attachments_fields
        fields = [
          {
            title: ref_type == "tag" ? s_("ChatMessage|Tag") : s_("ChatMessage|Branch"),
            value: ::Slack::Messenger::Util::LinkFormatter.format(ref_link),
            short: true
          },
          {
            title: s_("ChatMessage|Commit"),
            value: ::Slack::Messenger::Util::LinkFormatter.format(commit_link),
            short: true
          }
        ]

        fields << failed_stages_field if failed_stages.any?
        fields << failed_jobs_field if failed_jobs.any?
        fields << yaml_error_field if pipeline.has_yaml_errors?
        fields << pipeline_name_field if pipeline.name.present?

        fields
      end

      def message
        s_("ChatMessage|%{project_link}: Pipeline %{pipeline_link} of %{ref_type} %{ref_link} by %{user_combined_name} %{humanized_status} in %{duration}") %
          {
            project_link: project_link,
            pipeline_link: pipeline_link,
            ref_type: ref_type,
            ref_link: ref_link,
            user_combined_name: strip_markup(user_combined_name),
            humanized_status: humanized_status,
            duration: pretty_duration(duration)
          }
      end

      def humanized_status
        case status
        when 'success'
          detailed_status == "passed with warnings" ? s_("ChatMessage|has passed with warnings") : s_("ChatMessage|has passed")
        when 'failed'
          s_("ChatMessage|has failed")
        else
          status
        end
      end

      def ref_url
        if ref_type == 'tag'
          "#{project_url}/-/tags/#{ref}"
        else
          "#{project_url}/-/commits/#{ref}"
        end
      end

      def ref_link
        link(ref, ref_url)
      end

      def project_url
        project.web_url
      end

      def project_link
        link(project.name, project_url)
      end

      def pipeline_failed_jobs_url
        "#{project_url}/-/pipelines/#{pipeline_id}/failures"
      end

      def pipeline_url
        if failed_jobs.any?
          pipeline_failed_jobs_url
        else
          "#{project_url}/-/pipelines/#{pipeline_id}"
        end
      end

      def pipeline_link
        link("##{pipeline_id}", pipeline_url)
      end

      def job_url(job)
        "#{project_url}/-/jobs/#{job[:id]}"
      end

      def job_link(job)
        link(job[:name], job_url(job))
      end

      def failed_jobs_links
        failed = failed_jobs.slice(0, MAX_VISIBLE_JOBS)
        truncated = failed_jobs.slice(MAX_VISIBLE_JOBS, failed_jobs.size)

        failed_links = failed.map { |job| job_link(job) }

        unless truncated.blank?
          failed_links << (s_("ChatMessage|and [%{count} more](%{pipeline_failed_jobs_url})") % {
            count: truncated.size,
            pipeline_failed_jobs_url: pipeline_failed_jobs_url
          })
        end

        failed_links.join(I18n.t(:'support.array.words_connector'))
      end

      def stage_link(stage)
        # All stages link to the pipeline page
        link(stage, pipeline_url)
      end

      def failed_stages_links
        failed_stages.map { |s| stage_link(s) }.join(I18n.t(:'support.array.words_connector'))
      end

      def commit_url
        Gitlab::UrlBuilder.build(commit)
      end

      def commit_link
        link(commit.title, commit_url)
      end

      def author_url
        return unless user && committer

        Gitlab::UrlBuilder.build(committer)
      end
    end
  end
end
