# frozen_string_literal: true

require 'spec_helper'

describe ReleaseTools::ReleaseManagers::Schedule do
  let(:schedule) { described_class.new }
  let(:version) { ReleaseTools::Version.new('11.8') }

  let(:yaml) do
    <<~YAML
      - version: '11.8'
        date: November 16th, 2023
        manager_americas:
          - Robert Speicher
        manager_apac_emea:
          - Yorick Peterse
        appsec:
          - Security Member 1
          - Security Member 2
      - version: '11.9'
        date: December 21st, 2023
        manager_americas:
          - Robert Speicher
        manager_apac_emea:
          - New Team Member
        appsec:
          - Security Member 3
          - Security Member 4
      - version: '11.10'
        date: January 18th, 2024
        manager_americas:
          - Robert Speicher
        manager_apac_emea:
          - Yorick Peterse
        appsec:
          - Security Member 1
          - Security Member 4
    YAML
  end

  def stub_schedule(body)
    stub_request(:get, described_class::RELEASES_YAML).to_return(body: body)
  end

  before do
    # Prevent retry delay from slowing down specs
    stub_const("#{described_class}::RETRY_INTERVAL", 0)
  end

  describe '#version_for_date' do
    it 'calls the gitlab_releases gem' do
      expect(ReleaseTools::GitlabReleasesGemClient)
        .to receive(:version_for_date)
        .and_return('16.6')

      schedule.version_for_date(Date.today)
    end

    context 'when there are no releases scheduled at all' do
      it 'returns nil' do
        expect(ReleaseTools::GitlabReleasesGemClient)
          .to receive(:version_for_date)

        expect(schedule.version_for_date(Date.today)).to be_nil
      end
    end
  end

  describe '#ids_for_version' do
    context 'for authorized release managers' do
      let(:member1) { double(:member, name: 'Robert Speicher', id: 1, username: 'rspeicher') }
      let(:member2) { double(:member, name: 'Yorick Peterse', id: 2, username: 'yorickpeterse') }

      it 'returns an array of usernames' do
        allow(schedule)
          .to receive(:authorized_release_managers)
          .and_return([member1, member2])

        expect(schedule.ids_for_version(version)).to eq([1, 2])
      end
    end
  end

  describe '#usernames_for_version' do
    context 'for authorized release managers' do
      let(:member1) { double(:member, name: 'Robert Speicher', id: 1, username: 'rspeicher') }
      let(:member2) { double(:member, name: 'Yorick Peterse', id: 2, username: 'yorickpeterse') }

      it 'returns an array of usernames' do
        allow(schedule)
          .to receive(:authorized_release_managers)
          .and_return([member1, member2])

        expect(schedule.usernames_for_version(version)).to eq(%w(rspeicher yorickpeterse))
      end
    end
  end

  describe '#authorized_release_managers' do
    let(:version) { ReleaseTools::Version.new('11.9') }
    let(:release_managers_fixture) { File.expand_path('../../../fixtures/release_managers.yml', __dir__) }
    let(:definitions) { ReleaseTools::ReleaseManagers::Definitions.new(release_managers_fixture) }

    context 'for authorized release managers' do
      let(:member1) { double(:robert, name: 'Robert Speicher', id: 1, username: 'rspeicher') }
      let(:member2) { double(:new_team_member, name: 'New Team Member', id: 2, username: 'new-team-member') }

      it 'returns an array of users' do
        client = double('client')
        expect(client).to receive(:get_user).with('rspeicher').and_return(member1)
        expect(client).to receive(:get_user).with('new-team-member').and_return(member2)

        allow(ReleaseTools::ReleaseManagers::Client)
          .to receive(:new)
          .and_return(client)

        stub_schedule(yaml)

        expect(ReleaseTools::ReleaseManagers::Definitions).to receive(:new).and_return(definitions)

        expect(schedule.authorized_release_managers(version)).to contain_exactly(member1, member2)
      end
    end

    context 'when no release manager data is available' do
      it 'raises an error' do
        stub_schedule('')

        expect { schedule.authorized_release_managers(version) }
          .to raise_error(described_class::VersionNotFoundError)
      end
    end
  end

  describe '#group_members' do
    it 'returns a Hash mapping release manager names to their user attributes' do
      client = instance_spy(ReleaseTools::ReleaseManagers::Client)

      allow(ReleaseTools::ReleaseManagers::Client)
        .to receive(:new)
        .and_return(client)

      allow(client)
        .to receive(:members)
        .and_return([
          double(:member, name: 'Robert Speicher', id: 1, username: 'rspeicher'),
          double(:member, name: 'Yorick Peterse', id: 2, username: 'yorickpeterse')
        ])

      expect(schedule.group_members.fetch('Robert Speicher').id)
        .to eq(1)
      expect(schedule.group_members.fetch('Yorick Peterse').username)
        .to eq('yorickpeterse')
    end
  end

  describe '#schedule_yaml' do
    context 'when the download succeeds' do
      it 'returns the release manager data' do
        stub_schedule(yaml)

        expect(schedule.schedule_yaml.length).to eq(3)
      end
    end

    context 'when the download fails' do
      it 'returns an empty Array' do
        stub_request(:any, /.*/).to_raise(Errno::ENOENT)

        expect(schedule.schedule_yaml).to be_empty
      end
    end
  end

  describe '#active_release_managers' do
    it 'return the authorized release managers for the active milestone' do
      users = double('users')

      expect(schedule)
        .to receive(:active_version)
        .and_return(version)

      expect(schedule)
        .to receive(:authorized_release_managers)
        .with(version)
        .and_return(users)

      expect(schedule.active_release_managers).to eq(users)
    end
  end

  describe '#active_appsec_release_managers' do
    let(:user1) { build(:user, name: 'Security Member 1', id: 111) }
    let(:user2) { build(:user, name: 'Security Member 2', id: 222) }
    let(:user3) { build(:user, name: 'Security Member 3', id: 333) }
    let(:user4) { build(:user, name: 'Security Member 4', id: 444) }
    let(:user5) { build(:user, name: 'Security Member 5', id: 555) }

    let(:internal_client) do
      spy(
        group_members: [user1, user2, user3, user4, user5]
      )
    end

    subject { schedule.active_appsec_release_managers }

    it 'returns the active appsec release managers' do
      stub_schedule(yaml)

      allow(ReleaseTools::GitlabClient).to receive(:client).and_return(internal_client)
      allow(schedule).to receive(:active_version).and_return(ReleaseTools::Version.new('11.10'))

      expect(subject).to eq([user3, user4])
    end
  end

  describe '#active_version' do
    subject(:active_version) { schedule.active_version }

    it 'uses the GitlabReleasesGemClient to fetch the active_version' do
      expect(ReleaseTools::GitlabReleasesGemClient)
        .to receive(:active_version)
        .and_return(version.to_s)

      expect(schedule).not_to receive(:version_for_date)

      expect(active_version).to eq(version)
    end
  end
end
