# frozen_string_literal: true
require 'slack-notifier'

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]
      @failed_jobs = Array(data[:builds]).select { |b| b[:status] == 'failed' }.reverse # Show failed jobs from oldest to newest
      @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

      return [{ text: format(message), color: attachment_color }] unless fancy_notifications?

      [{
        fallback: format(message),
        color: attachment_color,
        author_name: 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: 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

    private

    def fancy_notifications?
      Feature.enabled?(:fancy_pipeline_slack_notifications, default_enabled: true)
    end

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

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

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

    def attachments_fields
      fields = [
        {
          title: ref_type == "tag" ? s_("ChatMessage|Tag") : s_("ChatMessage|Branch"),
          value: Slack::Notifier::LinkFormatter.format(ref_link),
          short: true
        },
        {
          title: s_("ChatMessage|Commit"),
          value: Slack::Notifier::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
    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: user_combined_name,
          humanized_status: humanized_status,
          duration: pretty_duration(duration)
        }
    end

    def humanized_status
      if fancy_notifications?
        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
      else
        case status
        when 'success'
          s_("ChatMessage|passed")
        when 'failed'
          s_("ChatMessage|failed")
        else
          status
        end
      end
    end

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

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

    def ref_link
      "[#{ref}](#{ref_url})"
    end

    def project_url
      project.web_url
    end

    def project_link
      "[#{project.name}](#{project_url})"
    end

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

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

    def pipeline_link
      "[##{pipeline_id}](#{pipeline_url})"
    end

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

    def job_link(job)
      "[#{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.translate(:'support.array.words_connector'))
    end

    def stage_link(stage)
      # All stages link to the pipeline page
      "[#{stage}](#{pipeline_url})"
    end

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

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

    def commit_link
      "[#{commit.title}](#{commit_url})"
    end

    def author_url
      return unless user && committer

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