require "rails_helper"

RSpec.describe "Onboardings" do
  let(:user) do
    create(:user,
           saw_onboarding: false,
           _skip_creating_profile: true,
           profile: create(:profile, location: "Llama Town"))
  end

  describe "GET /onboarding" do
    it "redirects user if unauthenticated" do
      get onboarding_url
      expect(response).to redirect_to("/magic_links/new")
    end

    it "return 200 when authenticated" do
      sign_in user
      get onboarding_url
      expect(response).to have_http_status(:ok)
    end

    it "contains proper data attribute keys" do
      sign_in user
      get onboarding_url
      expect(response.body).to include("data-community-description")
      expect(response.body).to include("data-community-logo")
      expect(response.body).to include("data-community-background-color")
      expect(response.body).to include("data-community-name")
    end

    it "contains proper data attribute values if the onboarding config is present" do
      sign_in user
      get onboarding_url

      expect(response.body).to include(Settings::Community.community_description)
    end
  end

  describe "GET /users_and_organizations" do
    context "when no suggestions are found" do
      it "returns an empty array (no automated suggested follow)" do
        sign_in user

        get users_and_organizations_onboarding_path

        expect(response).to have_http_status(:ok)
        expect(response.parsed_body).to eq([])
      end
    end

    context "when only user suggestions are found" do
      let(:tag) { create(:tag) }
      let(:other_user) { create(:user) }

      # Prepare auto-generated user suggestions
      before do
        user.follow(tag)
        create(:article, user: other_user, tags: [tag.name])
      end

      it "returns follow suggestions for an authenticated user" do
        sign_in user

        get users_and_organizations_onboarding_path

        response_user = response.parsed_body.first
        expect(response_user["id"]).to eq(other_user.id)
      end

      it "returns follow suggestions that have profile images" do
        sign_in user

        get users_and_organizations_onboarding_path

        response_user = response.parsed_body.first
        expect(response_user["profile_image_url"]).to eq(other_user.profile_image_url)
      end
    end

    context "when organization suggestions are found" do
      let(:suggested_orgs) { create_list(:organization, 2) }

      let(:expected_json_keys) do
        %w[id name username profile_image_url following summary type_identifier]
      end

      before do
        allow(FeatureFlag).to receive(:enabled?).and_return(true)
        allow(Organizations::SuggestProminent).to receive(:call).and_return(suggested_orgs)
      end

      it "returns organization follow suggestions for an authenticated user" do
        sign_in user

        get users_and_organizations_onboarding_path

        response_org_ids = response.parsed_body.pluck("id")
        expect(response_org_ids.size).to eq(2)

        suggested_org_ids = suggested_orgs.map(&:id)
        expect(response_org_ids).to match_array(suggested_org_ids)
      end

      it "returns organization follow suggestions that have profile images" do
        sign_in user

        get users_and_organizations_onboarding_path

        response_org = response.parsed_body.first
        expect(response_org.keys).to match_array(expected_json_keys)
        expect(response_org["profile_image_url"]).to eq(suggested_orgs.first.profile_image_url)
      end
    end

    context "when organization and user suggestions are found" do
      let(:suggested_users) { create_list(:user, 2) }
      let(:suggested_orgs) { create_list(:organization, 2) }

      let(:expected_json_keys) do
        %w[id name username profile_image_url following summary type_identifier]
      end

      before do
        allow(FeatureFlag).to receive(:enabled?).and_return(true)
        allow(Organizations::SuggestProminent).to receive(:call).and_return(suggested_orgs)
        allow(Users::SuggestProminent).to receive(:call).and_return(suggested_users)
      end

      it "returns users first, then organizations" do
        sign_in user

        get users_and_organizations_onboarding_path

        response_ids = response.parsed_body.pluck("id")
        expect(response_ids.size).to eq(4)

        suggested_ids = suggested_users.map(&:id) + suggested_orgs.map(&:id)
        expect(response_ids).to eq(suggested_ids)
      end
    end
  end

  describe "GET /onboarding/tags" do
    let(:headers) do
      {
        Accept: "application/json",
        "Content-Type": "application/json"
      }
    end

    before do
      sign_in user
      allow(Settings::General).to receive(:suggested_tags).and_return(%w[beginners javascript career])
    end

    it "returns tags" do
      create(:tag, name: Settings::General.suggested_tags.first)

      get tags_onboarding_path, headers: headers

      expect(response.parsed_body.size).to eq(1)
    end

    it "returns tags with the correct json representation" do
      tag = create(:tag, name: Settings::General.suggested_tags.first)

      get tags_onboarding_path, headers: headers

      response_tag = response.parsed_body.first
      expect(response_tag.keys).to \
        match_array(OnboardingsController::TAG_ONBOARDING_ATTRIBUTES.map(&:to_s))
      expect(response_tag["id"]).to eq(tag.id)
      expect(response_tag["name"]).to eq(tag.name)
      expect(response_tag["taggings_count"]).to eq(tag.taggings_count)
    end

    it "returns suggested and supported tags" do
      not_suggested_but_supported = create(:tag, name: "notsuggestedbutsupported", supported: true, suggested: false)
      neither_suggested_nor_supported = create(:tag, name: "definitelynotasuggestedtag", supported: false)

      get tags_onboarding_path, headers: headers

      expect(response.parsed_body.filter { |t| t["name"] == not_suggested_but_supported.name }).not_to be_empty
      expect(response.parsed_body.filter { |t| t["name"] == neither_suggested_nor_supported.name }).to be_empty
    end

    it "sets the correct edge caching surrogate key for all tags" do
      tag = create(:tag, name: Settings::General.suggested_tags.first)

      get tags_onboarding_path, headers: headers

      expected_key = ["tags", tag.record_key].to_set
      expect(response.headers["surrogate-key"].split.to_set).to eq(expected_key)
    end
  end

  describe "PATCH /onboarding" do
    context "when signed in" do
      before { sign_in user }

      it "updates the user's last_onboarding_page attribute" do
        params = { user: { last_onboarding_page: "v2: personal info form", username: "test" } }
        expect do
          patch "/onboarding", params: params
        end.to change(user, :last_onboarding_page)
      end

      it "updates the user's username attribute" do
        params = { user: { username: "WilhuffTarkin" } }
        expect do
          patch "/onboarding", params: params
        end.to change(user, :username).to("wilhufftarkin")
      end

      it "returns a 422 error if the username is blank" do
        params = { user: { username: "" } }
        patch "/onboarding", params: params
        expect(response).to have_http_status(:unprocessable_entity)
      end

      it "updates the user's profile" do
        params = { profile: { location: "Galactic Empire" } }
        expect do
          patch "/onboarding", params: params
        end.to change(user.profile, :location).to("Galactic Empire")
      end

      it "does not update the user's last_onboarding_page if it is empty" do
        params = { user: { last_onboarding_page: "" } }
        expect do
          patch "/onboarding", params: params
        end.not_to change(user, :last_onboarding_page)
      end
    end

    context "when signed out" do
      it "returns a not found error if user is not signed in" do
        patch "/onboarding.json", params: {}
        expect(response.parsed_body["error"]).to include("Please sign in")
      end
    end
  end

  describe "PATCH /onboarding/checkbox" do
    context "when signed in" do
      before { sign_in user }

      it "updates saw_onboarding boolean" do
        patch "/onboarding/checkbox.json", params: {}
        expect(user.saw_onboarding).to be(true)
      end

      it "updates checked_code_of_conduct and checked_terms_and_conditions" do
        patch "/onboarding/checkbox.json",
              params: {
                checked_code_of_conduct: "1",
                checked_terms_and_conditions: "1"
              }

        expect(user.checked_code_of_conduct).to be(true)
        expect(user.checked_terms_and_conditions).to be(true)
      end
    end

    context "when signed out" do
      it "returns a not found error if user is not signed in" do
        patch "/onboarding/checkbox.json", params: {}
        expect(response.parsed_body["error"]).to include("Please sign in")
      end
    end
  end

  describe "PATCH /onboarding/notifications" do
    before { sign_in user }

    it "updates onboarding checkbox" do
      user.update_column(:saw_onboarding, false)

      expect do
        patch notifications_onboarding_path(format: :json),
              params: { notifications: { tab: "notifications", email_newsletter: 1 } }
      end.to change { user.notification_setting.reload.email_newsletter }.from(false).to(true)
      expect(user.saw_onboarding).to be(true)
    end

    it "can toggle email_newsletter" do
      expect do
        patch notifications_onboarding_path(format: :json),
              params: { notifications: { tab: "notifications", email_newsletter: 1 } }
      end.to change { user.notification_setting.reload.email_newsletter }.from(false).to(true)

      expect do
        patch notifications_onboarding_path(format: :json),
              params: { notifications: { tab: "notifications", email_newsletter: 0 } }
      end.to change { user.notification_setting.reload.email_newsletter }.from(true).to(false)
    end

    it "can toggle email_digest_periodic" do
      expect do
        patch notifications_onboarding_path(format: :json),
              params: { notifications: { tab: "notifications", email_digest_periodic: 1 } }
      end.to change { user.notification_setting.reload.email_digest_periodic }.from(false).to(true)

      expect do
        patch notifications_onboarding_path(format: :json),
              params: { notifications: { tab: "notifications", email_digest_periodic: 0 } }
      end.to change { user.notification_setting.reload.email_digest_periodic }.from(true).to(false)
    end
  end

  describe "PATCH /onboarding/custom_actions" do
    let(:challenge_tag) { create(:tag, name: "devchallenge") }
    let(:education_tag) { create(:tag, name: "deved") }
    let(:featured_org) { create(:organization, username: "googleai") }

    before do
      sign_in user
      challenge_tag
      education_tag
      featured_org
      allow(FeatureFlag).to receive(:enabled?).with(:onboarding_custom_actions).and_return(true)
    end

    context "when following challenges" do
      it "follows the 'devchallenge' tag" do
        patch "/onboarding/custom_actions", params: { follow_challenges: true }, as: :json
        expect(Follow.where(followable_id: challenge_tag.id, followable_type: "ActsAsTaggableOn::Tag",
                            follower_id: user.id).size).to eq(1)
        expect(response).to have_http_status(:ok)
      end
    end

    context "when following education tracks" do
      it "follows the 'deved' tag" do
        patch "/onboarding/custom_actions", params: { follow_education_tracks: true }, as: :json
        expect(Follow.where(followable_id: education_tag.id, followable_type: "ActsAsTaggableOn::Tag",
                            follower_id: user.id).size).to eq(1)
        expect(response).to have_http_status(:ok)
      end
    end

    context "when following featured accounts" do
      it "follows the 'googleai' organization" do
        patch "/onboarding/custom_actions", params: { follow_featured_accounts: true }, as: :json
        expect(Follow.where(followable_id: featured_org.id, followable_type: "Organization",
                            follower_id: user.id).size).to eq(1)
        expect(response).to have_http_status(:ok)
      end
    end

    context "when multiple params are provided" do
      it "follows the appropriate tags and organizations" do
        patch "/onboarding/custom_actions", params: {
          follow_challenges: true,
          follow_education_tracks: true,
          follow_featured_accounts: true
        }, as: :json

        expect(Follow.where(followable_type: "ActsAsTaggableOn::Tag", follower_id: user.id).count).to eq(2)
        expect(Follow.where(followable_type: "Organization", follower_id: user.id).count).to eq(1)
        expect(response).to have_http_status(:ok)
      end
    end

    context "when params are false or not present" do
      it "does not follow any tags or organizations" do
        patch "/onboarding/custom_actions", params: {
          follow_challenges: false,
          follow_education_tracks: false,
          follow_featured_accounts: false
        }, as: :json

        expect(user.following_tags).to be_empty
        expect(user.following_organizations).to be_empty
        expect(response).to have_http_status(:ok)
      end
    end
  end

  describe "subforem selection in onboarding" do
    let(:user) { create(:user, saw_onboarding: false) }
    let(:subforem) { create(:subforem, discoverable: true, domain: "test.example.com") }
    let(:root_subforem) { create(:subforem, root: true, domain: "root.example.com") }

    before do
      sign_in user
    end

    it "shows subforem selection when user comes from a subforem" do
      # Simulate user coming from a specific subforem
      allow_any_instance_of(ApplicationController).to receive(:current_user).and_return(user)

      get "/onboarding"

      expect(response).to have_http_status(:ok)
      # Check that the onboarding container is present
      expect(response.body).to include("onboarding-container")
      # Check that the Onboarding JavaScript is loaded
      expect(response.body).to include("Onboarding")
    end

    it "includes subforem data in the onboarding page" do
      # Create some test subforems
      subforem
      root_subforem

      get "/onboarding"

      expect(response).to have_http_status(:ok)
      # Check that the onboarding container is present
      expect(response.body).to include("onboarding-container")
      # Check that the Onboarding JavaScript is loaded
      expect(response.body).to include("Onboarding")
    end
  end
end
