# frozen_string_literal: true

require 'rake_helper'

describe 'internal tasks', :rake do
  describe 'issue', task: 'internal:issue' do
    let(:issue) { instance_double(ReleaseTools::InternalRelease::Issue) }
    let(:version) { ReleaseTools::Version.new('15.2.6') }
    let(:coordinator) { instance_double(ReleaseTools::InternalRelease::Coordinator) }
    let(:notifier) { instance_double(ReleaseTools::Slack::InternalRelease::UnsupportedVersionNotifier) }
    let(:supported_versions) { ['15.2', '15.1'] }

    before do
      allow(issue).to receive_messages(title: 'title', description: 'description')

      allow(coordinator).to receive(:supported_minor_versions)
        .and_return(supported_versions)
    end

    context 'when no arguments are provided' do
      it 'calls the InternalRelease::Issue class with nil values' do
        expect(ReleaseTools::InternalRelease::Issue).to receive(:new)
          .with(iteration: nil)
          .and_return(issue)

        task.invoke
      end
    end

    context 'when only iteration is provided' do
      it 'calls the InternalRelease::Issue class with the iteration value' do
        expect(ReleaseTools::InternalRelease::Issue).to receive(:new)
          .with(iteration: '1')
          .and_return(issue)

        task.invoke('1')
      end
    end

    context 'when both iteration and a supported version are provided' do
      it 'checks for unsupported versions and creates an issue' do
        expect(ReleaseTools::GitlabReleasesGemClient)
          .to receive(:latest_patch_for_version)
          .with('15.2').and_return('15.2.6')

        expect(ReleaseTools::InternalRelease::Issue).to receive(:new)
          .with(iteration: '1', version: version)
          .and_return(issue)

        expect(ReleaseTools::Slack::InternalRelease::UnsupportedVersionNotifier)
        .to receive(:new).with('15.2').and_return(notifier)

        expect(notifier).to receive(:execute)

        task.invoke('1', '15.2')
      end
    end

    context 'when only version is provided' do
      it 'checks for unsupported versions and creates an issue with nil iteration' do
        expect(ReleaseTools::GitlabReleasesGemClient).to receive(:latest_patch_for_version)
          .with('15.2').and_return('15.2.6')

        expect(ReleaseTools::InternalRelease::Issue).to receive(:new)
          .with(iteration: nil, version: version)
          .and_return(issue)

        expect(ReleaseTools::Slack::InternalRelease::UnsupportedVersionNotifier)
        .to receive(:new).with('15.2').and_return(notifier)

        expect(notifier).to receive(:execute)

        task.invoke(nil, '15.2')
      end
    end
  end

  describe 'prepare:start', task: 'internal:prepare:start' do
    it 'starts the prepare stage' do
      expect(ReleaseTools::Slack::ReleasePipelineStartNotifier).to receive(:new)
        .with(release_type: :internal, stage: :start)
        .and_return(instance_double(ReleaseTools::Slack::ReleasePipelineStartNotifier, execute: true))

      task.invoke
    end
  end

  describe 'prepare:create_dedicated_issue', task: 'internal:prepare:create_dedicated_issue' do
    let(:versions) do
      [
        ReleaseTools::Version.new('15.2.1-internal4'),
        ReleaseTools::Version.new('15.1.2-internal4')
      ]
    end

    let(:issue) { instance_double(ReleaseTools::InternalRelease::DedicatedIssue) }

    before do
      allow(issue).to receive_messages(title: 'title', description: 'description')
    end

    it 'creates an internal release issue in the dedicated tracker' do
      versions_string = '15.2.1-internal4 15.1.2-internal4'

      ClimateControl.modify INTERNAL_VERSIONS: versions_string do
        expect(ReleaseTools::InternalRelease::DedicatedIssue).to receive(:new)
          .with(versions: versions)
          .and_return(issue)

        task.invoke
      end
    end
  end

  describe 'prepare:notify_dedicated', task: 'internal:prepare:notify_dedicated' do
    let(:versions) do
      [
        ReleaseTools::Version.new('15.2.1-internal4'),
        ReleaseTools::Version.new('15.1.2-internal4')
      ]
    end

    let(:issue) { instance_double(ReleaseTools::InternalRelease::DedicatedIssue) }

    it 'notifies dedicated slack channels that the internal release has started' do
      versions_string = '15.2.1-internal4 15.1.2-internal4'

      ClimateControl.modify INTERNAL_VERSIONS: versions_string do
        expect(ReleaseTools::Slack::InternalRelease::DedicatedStartNotifier).to receive(:new)
          .with(versions)
          .and_return(
            instance_double(
              ReleaseTools::Slack::InternalRelease::DedicatedStartNotifier, execute: true
            )
          )

        task.invoke
      end
    end
  end

  describe 'prepare:check_component_branch_pipeline_status',
           task: 'internal:prepare:check_component_branch_pipeline_status' do
    let(:versions) do
      [
        ReleaseTools::Version.new('15.2.1-internal4'),
        ReleaseTools::Version.new('15.1.2-internal4')
      ]
    end

    it 'ensures stable branches are green with the versions from VERSIONS env var' do
      versions_string = '15.2.1-internal4 15.1.2-internal4'

      ClimateControl.modify INTERNAL_VERSIONS: versions_string do
        expect(ReleaseTools::InternalRelease::Prepare::ComponentBranchVerifier).to receive(:new)
          .with(versions)
          .and_return(
            instance_double(
              ReleaseTools::InternalRelease::Prepare::ComponentBranchVerifier, execute: true
            )
          )

        task.invoke
      end
    end
  end

  describe 'release:start', task: 'internal:release:start' do
    it 'starts the release stage' do
      expect(ReleaseTools::Slack::ReleasePipelineStartNotifier).to receive(:new).with(release_type: :internal, stage: :release)
        .and_return(instance_double(ReleaseTools::Slack::ReleasePipelineStartNotifier, execute: true))

      task.invoke
    end
  end

  describe 'release:build_package', task: 'internal:release:build_package' do
    let(:iteration) { 5 }
    let(:version) { ReleaseTools::Version.new('15.2.1-internal5') }

    it 'performs the steps required to build a private package' do
      release = instance_double(ReleaseTools::InternalRelease::Release)
      allow(ReleaseTools::InternalRelease::Release).to receive(:new).and_return(release)

      expect(release).to receive(:execute)

      expect(ReleaseTools::InternalRelease::Release).to receive(:new)
        .with(version: version).and_return(release)

      task.invoke('15.2.1-internal5')
    end
  end

  describe 'release:generate_dynamic_pipeline', task: 'internal:release:generate_dynamic_pipeline' do
    let(:release_jobs) { instance_double(ReleaseTools::InternalRelease::ReleaseDynamicPipeline) }
    let(:generated_jobs) { "dummy:output" }

    before do
      allow(ReleaseTools::InternalRelease::ReleaseDynamicPipeline)
        .to receive(:new).with(versions).and_return(release_jobs)
    end

    context 'when INTERNAL_VERSIONS is set with multiple versions' do
      let(:versions) do
        [
          ReleaseTools::Version.new('15.2.1-internal8'),
          ReleaseTools::Version.new('15.1.2-internal8')
        ]
      end

      let(:versions_string) { '15.2.1-internal8 15.1.2-internal8' }

      it 'correctly processes multiple versions' do
        ClimateControl.modify INTERNAL_VERSIONS: versions_string do
          expect(ReleaseTools::InternalRelease::ReleaseDynamicPipeline)
            .to receive(:new).with(versions).and_return(release_jobs)

          expect(release_jobs).to receive(:generate).and_return(generated_jobs)

          expect(File).to receive(:write).with('dynamic-gitlab-ci.yml', generated_jobs)

          task.invoke
        end
      end
    end

    context 'when INTERNAL_VERSIONS is set with a single version' do
      let(:versions) { [ReleaseTools::Version.new('15.2.1-internal3')] }

      let(:version_string) { '15.2.1-internal3' }

      it 'correctly processes a single version' do
        ClimateControl.modify INTERNAL_VERSIONS: version_string do
          expect(ReleaseTools::InternalRelease::ReleaseDynamicPipeline)
            .to receive(:new).with(versions).and_return(release_jobs)

          expect(release_jobs).to receive(:generate).and_return(generated_jobs)

          expect(File).to receive(:write).with('dynamic-gitlab-ci.yml', generated_jobs)

          task.invoke
        end
      end
    end

    context 'when INTERNAL_VERSIONS is not set' do
      let(:versions) { nil }
      let(:version_string) { nil }

      it 'raises a NoMethodError' do
        ClimateControl.modify INTERNAL_VERSIONS: nil do
          expect { task.invoke }.to raise_error(
            NoMethodError
          )
        end
      end
    end
  end

  describe 'verify:start', task: 'internal:verify:start' do
    it 'starts the verify stage' do
      expect(ReleaseTools::Slack::ReleasePipelineStartNotifier).to receive(:new).with(release_type: :internal, stage: :verify)
        .and_return(instance_double(ReleaseTools::Slack::ReleasePipelineStartNotifier, execute: true))

      task.invoke
    end
  end

  describe 'verify:package_availability', task: 'internal:verify:package_availability' do
    let(:versions) do
      [
        ReleaseTools::Version.new('15.2.1-internal0'),
        ReleaseTools::Version.new('15.1.2-internal0')
      ]
    end

    let(:versions_string) { '15.2.1-internal0 15.1.2-internal0' }

    it 'checks if the package has been built and available on the pre-release channel' do
      check = instance_double(ReleaseTools::Services::OmnibusPackages::Tagging)
      allow(ReleaseTools::Services::OmnibusPackages::Tagging)
        .to receive(:new).and_return(check)

      expect(check).to receive(:execute).twice

      expect(ReleaseTools::Services::OmnibusPackages::Tagging).to receive(:new)
        .with(version: versions[0], package_type: :internal)
        .and_return(check)

      expect(ReleaseTools::Services::OmnibusPackages::Tagging).to receive(:new)
        .with(version: versions[1], package_type: :internal)
        .and_return(check)

      ClimateControl.modify INTERNAL_VERSIONS: versions_string do
        task.invoke
      end
    end
  end

  describe 'finalize:start', task: 'internal:finalize:start' do
    it 'starts the finalize stage' do
      expect(ReleaseTools::Slack::ReleasePipelineStartNotifier).to receive(:new).with(release_type: :internal, stage: :finalize)
        .and_return(instance_double(ReleaseTools::Slack::ReleasePipelineStartNotifier, execute: true))

      task.invoke
    end
  end

  describe 'finalize:notify_releases', task: 'internal:finalize:notify_releases' do
    let(:versions) do
      [
        ReleaseTools::Version.new('15.2.1-internal1'),
        ReleaseTools::Version.new('15.1.2-internal1')
      ]
    end

    let(:versions_string) { '15.2.1-internal1 15.1.2-internal1' }

    it 'Notify release managers that the internal packages are now available' do
      ClimateControl.modify INTERNAL_VERSIONS: versions_string do
        expect(ReleaseTools::InternalRelease::Finalize::ReleaseManagersNotifier)
          .to receive(:new)
          .with(versions)
          .and_return(
            instance_double(
              ReleaseTools::InternalRelease::Finalize::ReleaseManagersNotifier, execute: true
            )
          )

        task.invoke
      end
    end
  end

  describe 'finalize:notify_dedicated', task: 'internal:finalize:notify_dedicated' do
    it 'Notify Dedicated that the internal packages are now available' do
      expect(ReleaseTools::InternalRelease::Finalize::DedicatedNotifier)
        .to receive(:new)
        .and_return(
          instance_double(
            ReleaseTools::InternalRelease::Finalize::DedicatedNotifier, execute: true
          )
        )

      task.invoke
    end
  end
end
