# frozen_string_literal: true

require 'spec_helper'

RSpec.shared_examples 'staging and production' do
  subject(:perform_event) { service.perform_event(event) }

  let!(:service) { described_class.new }

  let(:deployments) { [build(:deployment, :success, sha: 'abcd1234')] }

  let(:gprd_version) do
    build(:product_version, auto_deploy_ref: '16.10.202402100400-abdf1234567.abcdef12345')
  end

  before do
    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:deployments)
      .and_return(deployments)

    allow(ReleaseTools::ProductVersion)
      .to receive(:each)
      .and_yield(ReleaseTools::ProductVersion.new('16.10.202402101000'))
      .and_yield(ReleaseTools::ProductVersion.new('16.10.202402100800'))
      .and_yield(ReleaseTools::ProductVersion.new('16.10.202402100400'))

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipelines)
      .with(ReleaseTools::Project::ReleaseTools, name: 'Coordinator pipeline', ref: '16.10.202402101000')
      .and_return([build(:pipeline, id: 10, ref: '16.10.202402101000')])

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipelines)
      .with(ReleaseTools::Project::ReleaseTools, name: 'Coordinator pipeline', ref: '16.10.202402100800')
      .and_return([build(:pipeline, id: 8, ref: '16.10.202402100800')])

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipelines)
      .with(ReleaseTools::Project::ReleaseTools, name: 'Coordinator pipeline', ref: '16.10.202402100400')
      .and_return([build(:pipeline, id: 6, ref: '16.10.202402100400')])

    allow(ReleaseTools::ProductVersion)
      .to receive(:from_metadata_sha)
      .with('abcd1234')
      .and_return(gprd_version)

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipeline_jobs)
      .with(ReleaseTools::Project::ReleaseTools, 10, scope: described_class::PROMOTE_JOB_VALID_STATES)
      .and_return(Gitlab::PaginatedResponse.new([]))

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipeline_jobs)
      .with(ReleaseTools::Project::ReleaseTools, 8, scope: described_class::PROMOTE_JOB_VALID_STATES)
      .and_return(Gitlab::PaginatedResponse.new([build(:job, name: 'promote')]))

    allow(ReleaseTools::GitlabOpsClient)
      .to receive(:pipeline_jobs)
      .with(ReleaseTools::Project::ReleaseTools, 6, scope: described_class::PROMOTE_JOB_VALID_STATES)
      .and_return(Gitlab::PaginatedResponse.new([build(:job, name: 'promote')]))
  end

  where(:locked_state_metric_value, :ready_state_metric_value, :baking_time_metric_value, :awaiting_promotion, :event, :validity, :new_state) do
    [
      ['0', '0', '0', '0', 'success', true, 'awaiting_promotion'],
      ['0', '0', '0', '0', 'start', true, 'locked'],
      ['1', '0', '0', '0', 'success', true, 'awaiting_promotion'],
      ['1', '0', '0', '0', 'start', true, 'locked'],
      ['0', '1', '0', '0', 'start', true, 'locked'],
      ['0', '1', '0', '0', 'success', false, 'ready'],
      ['0', '1', '0', '0', 'baking_complete', true, 'awaiting_promotion'],
      ['1', '0', '0', '0', 'baking_complete', false, 'locked']
    ]
  end

  with_them do
    it "returns validity" do
      expect(perform_event).to eq(validity)
      expect(service.current_state).to eq(new_state)
    end
  end

  context 'when there are no new packages available for promotion' do
    before do
      allow(ReleaseTools::GitlabOpsClient)
        .to receive(:pipeline_jobs)
        .with(ReleaseTools::Project::ReleaseTools, 8, scope: described_class::PROMOTE_JOB_VALID_STATES)
        .and_return(Gitlab::PaginatedResponse.new([]))
    end

    where(:locked_state_metric_value, :ready_state_metric_value, :baking_time_metric_value, :awaiting_promotion, :event, :validity, :new_state) do
      [
        ['0', '0', '0', '0', 'success', true, 'ready'],
        ['1', '0', '0', '0', 'success', true, 'ready'],
        ['0', '1', '0', '0', 'baking_complete', false, 'ready']
      ]
    end

    with_them do
      it "returns validity and moves state" do
        expect(perform_event).to eq(validity)
        expect(service.current_state).to eq(new_state)
      end
    end
  end
end

# Stub query to Prometheus for `auto_deploy_environment_state` metric
RSpec.shared_context 'environment state transition context' do |environment, stage|
  let(:query_string) { "auto_deploy_environment_state{target_env=\"#{environment}\",target_stage=\"#{stage}\"}" }

  let(:query_spy) { instance_spy(ReleaseTools::Prometheus::Query) }

  let(:results) do
    [
      {
        "metric" => {
          "__name__" => "auto_deploy_environment_state",
          "target_env" => "gstg",
          "target_stage" => "cny",
          "env_state" => 'ready'
        },
        "value" => [1_435_781_451.781, ready_state_metric_value]
      },
      {
        "metric" => {
          "__name__" => "auto_deploy_environment_state",
          "target_env" => "gstg",
          "target_stage" => "cny",
          "env_state" => 'locked'
        },
        "value" => [1_435_781_451.781, locked_state_metric_value]
      }
    ]
  end

  let(:response) do
    {
      "status" => "success",
      "data" => {
        "resultType" => "vector",
        "result" => results
      }
    }
  end

  before do
    allow(ReleaseTools::Prometheus::Query).to receive(:new).and_return(query_spy)

    allow(query_spy).to receive(:run).with(query_string).and_return(response)
  end
end

RSpec.shared_examples 'environment state transition' do |environment, stage|
  include_context 'metric registry'
  include_context 'environment state transition context', environment, stage

  let(:new_state) { 'ready' }

  let(:ready_state_metric_value) { '0' }
  let(:locked_state_metric_value) { '1' }

  describe '#current_state' do
    subject(:current_state) { described_class.new.current_state }

    context 'with non nil current state' do
      it 'returns current_state' do
        expect(current_state).to eq('locked')
      end
    end

    context 'with nil state' do
      let(:locked_state_metric_value) { '0' }

      it 'returns initial state' do
        expect(current_state).to eq('initial')
      end
    end
  end
end
