# typed: false
require "rails_helper"

RSpec.describe GlobalStatesController, type: :controller do
  let(:admin_user) { create(:user, role: :admin) }
  let(:valid_attributes) do
    { key: "test_key", value: "test_value", value_type: "string" }
  end
  let(:invalid_attributes) { { key: "", value: "", value_type: "" } }
  let(:global_state) { create(:global_state, valid_attributes) }

  before { sign_in admin_user }

  describe "GET #index" do
    it "returns a success response" do
      get :index
      expect(response).to be_successful
    end
  end

  describe "GET #new" do
    it "returns a success response" do
      get :new
      expect(response).to be_successful
    end
  end

  describe "POST #create" do
    context "with valid params" do
      it "creates a new GlobalState" do
        expect {
          post :create, params: { global_state: valid_attributes }
        }.to change(GlobalState, :count).by(1)
      end

      it "redirects to the global states list" do
        post :create, params: { global_state: valid_attributes }
        expect(response).to redirect_to(global_states_path)
      end
    end

    context "with invalid params" do
      it "returns unprocessable entity status" do
        post :create, params: { global_state: invalid_attributes }
        expect(response).to have_http_status(:unprocessable_entity)
      end
    end
  end

  describe "GET #edit" do
    it "returns a success response" do
      get :edit, params: { id: global_state.to_param }
      expect(response).to be_successful
    end
  end

  describe "PUT #update" do
    context "with valid params" do
      let(:new_attributes) { { value: "new_value" } }

      it "updates the requested global_state" do
        put :update,
            params: {
              id: global_state.to_param,
              global_state: new_attributes,
            }
        global_state.reload
        expect(global_state.value).to eq("new_value")
      end

      it "redirects to the global states list" do
        put :update,
            params: {
              id: global_state.to_param,
              global_state: new_attributes,
            }
        expect(response).to redirect_to(global_states_path)
      end
    end

    context "with invalid params" do
      it "returns unprocessable entity status" do
        put :update,
            params: {
              id: global_state.to_param,
              global_state: invalid_attributes,
            }
        expect(response).to have_http_status(:unprocessable_entity)
      end
    end
  end

  describe "DELETE #destroy" do
    let!(:global_state) { create(:global_state) }

    it "destroys the requested global_state" do
      expect {
        delete :destroy, params: { id: global_state.to_param }
      }.to change(GlobalState, :count).by(-1)
    end

    it "redirects to the global_states list" do
      delete :destroy, params: { id: global_state.to_param }
      expect(response).to redirect_to(global_states_path)
    end
  end

  describe "FA Cookies Management" do
    let(:fa_cookie_keys) do
      %w[furaffinity-cookie-a furaffinity-cookie-b furaffinity-cookie-oaid]
    end

    describe "GET #fa_cookies" do
      context "when no FA cookie states exist" do
        before { GlobalState.delete_all }

        it "returns a success response" do
          get :fa_cookies
          expect(response).to be_successful
        end

        it "assigns new GlobalState objects for all required cookies" do
          get :fa_cookies
          assigned_cookies = assigns(:fa_cookies)

          expect(assigned_cookies.size).to eq(3)
          expect(assigned_cookies).to all(be_new_record)
          expect(assigned_cookies.map(&:key)).to match_array(fa_cookie_keys)
          expect(assigned_cookies.map(&:value_type).uniq).to eq(["string"])
        end
      end

      context "when FA cookie states exist" do
        it "assigns all FA cookie states" do
          cookie_states =
            fa_cookie_keys.map do |key|
              create(
                :global_state,
                key: key,
                value: "test",
                value_type: :string,
              )
            end

          get :fa_cookies
          expect(assigns(:fa_cookies)).to match_array(cookie_states)
        end
      end
    end

    describe "GET #edit_fa_cookies" do
      context "when no FA cookie states exist" do
        before { GlobalState.delete_all }

        it "returns a success response" do
          get :edit_fa_cookies
          expect(response).to be_successful
        end

        it "assigns new GlobalState objects for all required cookies" do
          get :edit_fa_cookies
          assigned_cookies = assigns(:fa_cookies)

          expect(assigned_cookies.size).to eq(3)
          expect(assigned_cookies).to all(be_new_record)
          expect(assigned_cookies.map(&:key)).to match_array(fa_cookie_keys)
          expect(assigned_cookies.map(&:value_type).uniq).to eq(["string"])
        end
      end

      context "when FA cookie states exist" do
        it "assigns all FA cookie states" do
          cookie_states =
            fa_cookie_keys.map do |key|
              create(
                :global_state,
                key: key,
                value: "test",
                value_type: :string,
              )
            end

          get :edit_fa_cookies
          expect(assigns(:fa_cookies)).to match_array(cookie_states)
        end
      end
    end

    describe "PATCH #update_fa_cookies" do
      context "when no FA cookie states exist" do
        before { GlobalState.delete_all }

        let(:new_values) { fa_cookie_keys.index_with { |_key| "new_value" } }

        it "creates all cookie states" do
          expect {
            patch :update_fa_cookies, params: { fa_cookies: new_values }
          }.to change(GlobalState, :count).by(3)

          fa_cookie_keys.each do |key|
            state = GlobalState.find_by(key: key)
            expect(state).to be_present
            expect(state.value).to eq("new_value")
            expect(state.value_type).to eq("string")
          end
        end

        it "redirects to the fa_cookies page" do
          patch :update_fa_cookies, params: { fa_cookies: new_values }
          expect(response).to redirect_to(fa_cookies_global_states_path)
        end
      end

      context "when FA cookie states exist" do
        let(:new_values) { fa_cookie_keys.index_with { |_key| "new_value" } }

        it "updates existing records" do
          existing_states =
            fa_cookie_keys.map do |key|
              create(
                :global_state,
                key: key,
                value: "old_value",
                value_type: :string,
              )
            end

          patch :update_fa_cookies, params: { fa_cookies: new_values }

          existing_states.each do |state|
            expect(state.reload.value).to eq("new_value")
          end
        end
      end

      context "with invalid params" do
        let(:invalid_values) { { "furaffinity-cookie-a" => "" } }

        it "returns unprocessable entity status" do
          patch :update_fa_cookies, params: { fa_cookies: invalid_values }
          expect(response).to have_http_status(:unprocessable_entity)
        end

        it "assigns @fa_cookies with all required keys" do
          patch :update_fa_cookies, params: { fa_cookies: invalid_values }
          assigned_cookies = assigns(:fa_cookies)

          expect(assigned_cookies).to be_present
          expect(assigned_cookies.map(&:key)).to match_array(fa_cookie_keys)
        end
      end
    end
  end

  describe "Inkbunny Credentials Management" do
    let(:ib_cookie_keys) do
      %w[inkbunny-username inkbunny-password inkbunny-sid]
    end

    describe "GET #ib_cookies" do
      context "when no Inkbunny credentials exist" do
        before { GlobalState.delete_all }

        it "returns a success response" do
          get :ib_cookies
          expect(response).to be_successful
        end

        it "assigns new GlobalState objects for all required credentials" do
          get :ib_cookies
          assigned_credentials = assigns(:ib_cookies)

          expect(assigned_credentials.size).to eq(3)
          expect(assigned_credentials).to all(be_new_record)
          expect(assigned_credentials.map(&:key)).to match_array(ib_cookie_keys)
          expect(assigned_credentials.map(&:value_type).uniq).to eq(["string"])
        end
      end

      context "when Inkbunny credentials exist" do
        it "assigns all Inkbunny credentials" do
          username = create(:global_state, :inkbunny_username)
          password = create(:global_state, :inkbunny_password)
          sid = create(:global_state, :inkbunny_sid)

          get :ib_cookies
          expect(assigns(:ib_cookies)).to match_array([username, password, sid])
        end
      end
    end

    describe "GET #edit_ib_cookies" do
      context "when no Inkbunny credentials exist" do
        before { GlobalState.delete_all }

        it "returns a success response" do
          get :edit_ib_cookies
          expect(response).to be_successful
        end

        it "assigns new GlobalState objects for editable credentials" do
          get :edit_ib_cookies
          assigned_credentials = assigns(:ib_cookies)

          expect(assigned_credentials.size).to eq(2)
          expect(assigned_credentials).to all(be_new_record)
          expect(assigned_credentials.map(&:key)).to match_array(
            %w[inkbunny-username inkbunny-password],
          )
          expect(assigned_credentials.map(&:value_type).uniq).to eq(["string"])
        end

        it "has a valid route" do
          expect(ib_cookies_edit_global_states_path).to eq(
            "/state/ib-cookies/edit",
          )
        end
      end

      context "when Inkbunny credentials exist" do
        it "assigns editable credentials and session ID" do
          username = create(:global_state, :inkbunny_username)
          password = create(:global_state, :inkbunny_password)
          sid = create(:global_state, :inkbunny_sid)

          get :edit_ib_cookies
          expect(assigns(:ib_cookies)).to match_array([username, password])
          expect(assigns(:ib_sid)).to eq(sid)
        end
      end
    end

    describe "PATCH #update_ib_cookies" do
      context "with valid parameters" do
        let(:valid_params) do
          {
            ib_cookies: {
              "inkbunny-username" => "newuser",
              "inkbunny-password" => "newpass",
            },
          }
        end

        it "creates or updates the credentials" do
          patch :update_ib_cookies, params: valid_params

          username = GlobalState.find_by(key: "inkbunny-username")
          password = GlobalState.find_by(key: "inkbunny-password")

          expect(username.value).to eq("newuser")
          expect(username.value_type).to eq("string")
          expect(password.value).to eq("newpass")
          expect(password.value_type).to eq("string")
        end

        it "redirects to the credentials page" do
          patch :update_ib_cookies, params: valid_params
          expect(response).to redirect_to(ib_cookies_global_states_path)
        end

        it "sets a success notice" do
          patch :update_ib_cookies, params: valid_params
          expect(flash[:notice]).to eq(
            "Inkbunny credentials were successfully updated.",
          )
        end
      end

      context "with invalid parameters" do
        let(:invalid_params) do
          {
            ib_cookies: {
              "inkbunny-username" => "",
              "inkbunny-password" => "",
            },
          }
        end

        it "does not create new credentials" do
          expect {
            patch :update_ib_cookies, params: invalid_params
          }.not_to change(GlobalState, :count)
        end

        it "renders the edit template" do
          patch :update_ib_cookies, params: invalid_params
          expect(response).to render_template(:edit_ib_cookies)
        end

        it "sets an error alert" do
          patch :update_ib_cookies, params: invalid_params
          expect(flash.now[:alert]).to match(
            /Error updating Inkbunny credentials:/,
          )
        end
      end

      context "when trying to set both credentials and session ID" do
        let(:invalid_params) do
          {
            ib_cookies: {
              "inkbunny-username" => "user",
              "inkbunny-password" => "pass",
              "inkbunny-sid" => "sid123",
            },
          }
        end

        it "does not update any credentials" do
          expect {
            patch :update_ib_cookies, params: invalid_params
          }.not_to change(GlobalState, :count)
        end

        it "renders the edit template" do
          patch :update_ib_cookies, params: invalid_params
          expect(response).to render_template(:edit_ib_cookies)
        end

        it "sets an appropriate error message" do
          patch :update_ib_cookies, params: invalid_params
          expect(flash.now[:alert]).to match(
            /Cannot set both credentials and session ID at the same time/,
          )
        end
      end

      context "when setting only session ID" do
        let(:sid_params) { { ib_cookies: { "inkbunny-sid" => "newsid123" } } }

        it "updates the session ID and preserves credentials" do
          username = create(:global_state, :inkbunny_username, value: "olduser")
          password = create(:global_state, :inkbunny_password, value: "oldpass")

          patch :update_ib_cookies, params: sid_params

          expect(GlobalState.find_by(key: "inkbunny-sid").value).to eq(
            "newsid123",
          )
          expect(username.reload.value).to eq("olduser")
          expect(password.reload.value).to eq("oldpass")
        end

        it "redirects to the credentials page" do
          patch :update_ib_cookies, params: sid_params
          expect(response).to redirect_to(ib_cookies_global_states_path)
        end
      end

      context "when setting only credentials" do
        let(:credentials_params) do
          {
            ib_cookies: {
              "inkbunny-username" => "newuser",
              "inkbunny-password" => "newpass",
            },
          }
        end

        it "updates the credentials and preserves session ID" do
          sid = create(:global_state, :inkbunny_sid, value: "oldsid")

          patch :update_ib_cookies, params: credentials_params

          username = GlobalState.find_by(key: "inkbunny-username")
          password = GlobalState.find_by(key: "inkbunny-password")
          expect(username.value).to eq("newuser")
          expect(password.value).to eq("newpass")
          expect(sid.reload.value).to eq("oldsid")
        end

        it "redirects to the credentials page" do
          patch :update_ib_cookies, params: credentials_params
          expect(response).to redirect_to(ib_cookies_global_states_path)
        end
      end
    end
  end
end
