# frozen_string_literal: true

describe "Admin upcoming changes" do
  fab!(:current_user, :admin)
  let(:upcoming_changes_page) { PageObjects::Pages::AdminUpcomingChanges.new }

  before do
    mock_upcoming_change_metadata(
      {
        enable_upload_debug_mode: {
          impact: "other,developers",
          status: :experimental,
          impact_type: "other",
          impact_role: "developers",
        },
        about_page_extra_groups_show_description: {
          impact: "feature,all_members",
          status: :stable,
          impact_type: "feature",
          impact_role: "all_members",
        },
      },
    )

    SiteSetting.about_page_extra_groups_show_description = false
    sign_in(current_user)
  end

  it "shows a list of upcoming changes and their metadata" do
    upcoming_changes_page.visit
    expect(upcoming_changes_page).to have_change(:about_page_extra_groups_show_description)
    expect(upcoming_changes_page).to have_change(:enable_upload_debug_mode)

    expect(
      upcoming_changes_page.change_item(:about_page_extra_groups_show_description),
    ).to have_status(:stable)
    expect(
      upcoming_changes_page.change_item(:about_page_extra_groups_show_description),
    ).to have_impact_role(:all_members)
  end

  it "does not show conceptual upcoming changes" do
    mock_upcoming_change_metadata(
      {
        enable_upload_debug_mode: {
          impact: "other,developers",
          status: :experimental,
          impact_type: "other",
          impact_role: "developers",
        },
        about_page_extra_groups_show_description: {
          impact: "feature,all_members",
          status: :conceptual,
          impact_type: "feature",
          impact_role: "all_members",
        },
      },
    )

    upcoming_changes_page.visit
    expect(upcoming_changes_page).to have_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_no_change(:about_page_extra_groups_show_description)
  end

  it "does not show permanent upcoming changes" do
    mock_upcoming_change_metadata(
      {
        allow_uppercase_posts: {
          impact: "feature,all_members",
          status: :permanent,
          impact_type: "feature",
          impact_role: "all_members",
        },
      },
    )

    upcoming_changes_page.visit
    expect(upcoming_changes_page).to have_no_change(:allow_uppercase_posts)
  end

  # NOTE (martin): Skipped for now because it is flaky on CI, it will be something to do with the
  # sample plugin settings loaded in the SiteSetting model.
  xit "shows upcoming changes from plugins" do
    upcoming_changes_page.visit
    expect(upcoming_changes_page).to have_change(:enable_experimental_sample_plugin_feature)
    expect(
      upcoming_changes_page.change_item(:enable_experimental_sample_plugin_feature),
    ).to have_plugin_name("Sample plugin")
  end

  it "can enable and disable an upcoming change using the dropdown" do
    upcoming_changes_page.visit

    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_disabled
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("everyone")
    expect(upcoming_changes_page).to have_enabled_for_success_toast("everyone")
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled
    expect(SiteSetting.enable_upload_debug_mode).to be_truthy

    # Revisit the page to skip the rate limit
    upcoming_changes_page.visit
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("no_one")
    expect(upcoming_changes_page).to have_disabled_success_toast
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_disabled

    expect(SiteSetting.enable_upload_debug_mode).to be_falsey
  end

  it "tests different enabled_for options behavior" do
    upcoming_changes_page.visit

    # Add a group to test clearing behavior
    SiteSetting.enable_upload_debug_mode = true
    Fabricate(
      :site_setting_group,
      name: "enable_upload_debug_mode",
      group_ids: Group::AUTO_GROUPS[:trust_level_4].to_s,
    )

    # Refresh after setting up the group
    upcoming_changes_page.visit
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to have_groups(
      "trust_level_4",
    )
    expect(UpcomingChanges.has_groups?(:enable_upload_debug_mode)).to be_truthy

    # Test 'no_one' option - should disable the change and clear groups
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("no_one")
    expect(upcoming_changes_page).to have_disabled_success_toast
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_disabled

    upcoming_changes_page.visit
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to have_no_group_selector
    expect(SiteSetting.enable_upload_debug_mode).to be_falsey
    expect(UpcomingChanges.has_groups?(:enable_upload_debug_mode)).to be_falsey

    # Test 'everyone' option - should enable the change and clear groups
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("everyone")
    expect(upcoming_changes_page).to have_enabled_for_success_toast("everyone")
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled

    upcoming_changes_page.visit
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to have_no_group_selector
    expect(SiteSetting.enable_upload_debug_mode).to be_truthy

    # Test 'staff' option - should enable the change and set staff group
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("staff")
    expect(upcoming_changes_page).to have_enabled_for_success_toast(
      "staff",
      translation_args: {
        staffGroupName: I18n.t("groups.default_names.staff").titleize,
      },
    )
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled

    upcoming_changes_page.visit
    expect(UpcomingChanges.has_groups?(:enable_upload_debug_mode)).to be_truthy
    expect(SiteSetting.enable_upload_debug_mode).to be_truthy

    # Test 'groups' option - should not change enabled state until groups are selected and saved
    upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for("groups")
    upcoming_changes_page.change_item(:enable_upload_debug_mode).add_group("trust_level_4")
    upcoming_changes_page.change_item(:enable_upload_debug_mode).save_groups
    expect(upcoming_changes_page).to have_enabled_for_success_toast(
      "specific_groups_with_group_names",
      translation_args: {
        groupNames: "staff, trust_level_4",
        count: 2,
      },
    )

    upcoming_changes_page.visit
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to have_groups(
      "staff",
      "trust_level_4",
    )
    expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled
    expect(UpcomingChanges.has_groups?(:enable_upload_debug_mode)).to be_truthy
    expect(SiteSetting.enable_upload_debug_mode).to be_truthy
  end

  it "can filter by name, description, plugin, status, impact type, or enabled/disabled" do
    upcoming_changes_page.visit

    # Filter by name
    upcoming_changes_page.filter_controls.type_in_search("upload debug")

    expect(upcoming_changes_page).to have_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_no_change(:about_page_extra_groups_show_description)

    upcoming_changes_page.filter_controls.clear_search

    # Filter by plugin
    # NOTE (martin): Skipped for now because it is flaky on CI, it will be something to do with the
    # sample plugin settings loaded in the SiteSetting model.
    # upcoming_changes_page.filter_controls.type_in_search("sample plugin")

    # expect(upcoming_changes_page).to have_change(:enable_experimental_sample_plugin_feature)
    # expect(upcoming_changes_page).to have_no_change(:about_page_extra_groups_show_description)

    # upcoming_changes_page.filter_controls.clear_search

    upcoming_changes_page.filter_controls.toggle_dropdown_filters

    # Filter by status
    upcoming_changes_page.filter_controls.select_dropdown_option("Stable", dropdown_id: "status")

    expect(upcoming_changes_page).to have_no_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_change(:about_page_extra_groups_show_description)

    upcoming_changes_page.filter_controls.select_all_dropdown_option(dropdown_id: "status")

    # Filter by impact type
    upcoming_changes_page.filter_controls.select_dropdown_option("Feature", dropdown_id: "type")

    expect(upcoming_changes_page).to have_no_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_change(:about_page_extra_groups_show_description)

    upcoming_changes_page.filter_controls.select_all_dropdown_option(dropdown_id: "type")

    # Filter by impact role
    upcoming_changes_page.filter_controls.select_dropdown_option(
      "Developers",
      dropdown_id: "impactRole",
    )

    expect(upcoming_changes_page).to have_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_no_change(:about_page_extra_groups_show_description)

    upcoming_changes_page.filter_controls.select_all_dropdown_option(dropdown_id: "impactRole")

    # Filter by enabled/disabled
    upcoming_changes_page.filter_controls.select_dropdown_option("Enabled", dropdown_id: "enabled")

    expect(upcoming_changes_page).to have_no_change(:enable_upload_debug_mode)
    expect(upcoming_changes_page).to have_no_change(:about_page_extra_groups_show_description)

    upcoming_changes_page.filter_controls.select_all_dropdown_option(dropdown_id: "enabled")
  end

  it "displays a notification dot on the sidebar and clears it when navigating to upcoming changes" do
    sidebar = PageObjects::Components::NavigationMenu::Sidebar.new

    UpcomingChangeEvent.create!(
      event_type: :added,
      upcoming_change_name: "enable_upload_debug_mode",
    )

    visit "/admin"
    sidebar.toggle_all_sections

    expect(sidebar.find_section_link("admin_upcoming_changes")).to have_css(
      ".sidebar-section-link-suffix.admin-sidebar-nav-link__dot",
    )

    sidebar.find_section_link("admin_upcoming_changes").click

    expect(sidebar.find_section_link("admin_upcoming_changes")).to have_no_css(
      ".sidebar-section-link-suffix.admin-sidebar-nav-link__dot",
    )
  end

  it "does not display a notification dot when there are no new added events" do
    sidebar = PageObjects::Components::NavigationMenu::Sidebar.new

    current_user.custom_fields["last_visited_upcoming_changes_at"] = Time.current.iso8601
    current_user.save_custom_fields

    visit "/admin"
    sidebar.toggle_all_sections

    expect(sidebar.find_section_link("admin_upcoming_changes")).to have_no_css(
      ".sidebar-section-link-suffix.admin-sidebar-nav-link__dot",
    )
  end

  context "when the staff group name has been localized" do
    before do
      SiteSetting.default_locale = "de"
      Group.refresh_automatic_group!(:staff)
    end

    it "displays the localized name in the enabled for options and enabling staff works correctly" do
      upcoming_changes_page.visit

      upcoming_changes_page.change_item(:enable_upload_debug_mode).select_enabled_for(
        Group.find(Group::AUTO_GROUPS[:staff]).name,
      )
      expect(upcoming_changes_page).to have_enabled_for_success_toast(
        "staff",
        translation_args: {
          staffGroupName: I18n.t("groups.default_names.staff", locale: SiteSetting.default_locale),
        },
      )
      expect(upcoming_changes_page.change_item(:enable_upload_debug_mode)).to be_enabled
      expect(SiteSetting.enable_upload_debug_mode).to be_truthy
      expect(SiteSettingGroup.find_by(name: "enable_upload_debug_mode").group_ids).to include(
        Group::AUTO_GROUPS[:staff].to_s,
      )

      upcoming_changes_page.visit
      expect(upcoming_changes_page.change_item(:enable_upload_debug_mode).enabled_for).to eq(
        Group.find(Group::AUTO_GROUPS[:staff]).name,
      )
    end
  end

  context "when the upcoming change has a default override" do
    let(:settings_page) { PageObjects::Pages::AdminSiteSettings.new }

    before do
      mock_upcoming_change_metadata(
        {
          enable_upload_debug_mode: {
            impact: "other,developers",
            status: :experimental,
            impact_type: "other",
            impact_role: "developers",
          },
        },
      )
      mock_upcoming_change_default_overrides(
        {
          suggested_topics_max_days_old: {
            upcoming_change: :enable_upload_debug_mode,
            new_default: 1000,
          },
        },
      )
      SiteSetting.enable_upload_debug_mode = true
      SiteSetting.refresh!
    end

    it "shows information about the default override in the site settings UI" do
      settings_page.visit("suggested_topics_max_days_old")
      expect(settings_page).to have_upcoming_change_default_warning(
        :suggested_topics_max_days_old,
        old_default: 365,
        new_default: 1000,
      )
    end
  end

  context "when the upcoming change has a boolean default override and the admin opts out" do
    let(:settings_page) { PageObjects::Pages::AdminSiteSettings.new }

    before do
      mock_upcoming_change_metadata(
        {
          enable_upload_debug_mode: {
            impact: "site_setting_default,all_members",
            status: :experimental,
            impact_type: "site_setting_default",
            impact_role: "all_members",
          },
        },
      )
      mock_upcoming_change_default_overrides(
        {
          limit_suggested_to_category: {
            upcoming_change: :enable_upload_debug_mode,
            new_default: true,
          },
        },
      )
      SiteSetting.enable_upload_debug_mode = true
      SiteSetting.refresh!
    end

    after do
      clear_mocked_upcoming_change_metadata
      clear_mocked_upcoming_change_default_overrides
    end

    it "preserves the admin's opt-out across page reload while still showing the override warning" do
      settings_page.visit("limit_suggested_to_category")

      expect(settings_page).to have_upcoming_change_default_warning(
        :limit_suggested_to_category,
        old_default: false,
        new_default: true,
      )
      expect(
        settings_page.find_setting(:limit_suggested_to_category).find(
          ".setting-value input[type=checkbox]",
        ),
      ).to be_checked

      settings_page.toggle_setting(:limit_suggested_to_category)

      # Wait for the save to land in the DB before forcing the in-process
      # SiteSetting refresh below — the .overridden class only appears once
      # the value has been persisted.
      expect(
        settings_page.find_setting(:limit_suggested_to_category, overridden: true),
      ).to be_present

      # Production reproduces the bug because each unicorn worker re-runs
      # SiteSetting.refresh! via the MessageBus subscriber after another
      # worker persists a setting change. In a single-process system spec
      # that subscriber doesn't fire, so we trigger the refresh explicitly
      # to mirror what happens on the next request in a real deployment.
      SiteSetting.refresh!

      page.refresh

      expect(settings_page).to have_upcoming_change_default_warning(
        :limit_suggested_to_category,
        old_default: false,
        new_default: true,
      )
      expect(
        settings_page.find_setting(:limit_suggested_to_category).find(
          ".setting-value input[type=checkbox]",
        ),
      ).not_to be_checked
    end
  end
end
