describe Administrateurs::IneligibiliteRulesController, type: :controller do
  include Logic
  let(:user) { create(:user) }
  let(:admin) { create(:administrateur, user: create(:user)) }
  let(:procedure) { create(:procedure, administrateurs: [admin], types_de_champ_public:) }
  let(:types_de_champ_public) { [] }

  describe 'condition management' do
    before { sign_in(admin.user) }

    let(:default_params) do
      {
        procedure_id: procedure.id,
        revision_id: procedure.draft_revision.id
      }
    end

    describe '#add_row' do
      subject { post :add_row, params: default_params, format: :turbo_stream }

      context 'without any row' do
        it 'creates an empty condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(nil)
            .to(empty_operator(empty, empty))
        end
      end

      context 'with row' do
        before do
          procedure.draft_revision.ineligibilite_rules = empty_operator(empty, empty)
          procedure.draft_revision.save!
        end

        it 'add one more creates an empty condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(empty_operator(empty, empty))
            .to(ds_and([
              empty_operator(empty, empty),
              empty_operator(empty, empty)
            ]))
        end
      end
    end

    describe 'delete_row' do
      let(:condition_form) do
        {
          top_operator_name: Logic::And.name,
          rows: [
            {
              targeted_champ: empty.to_json,
              operator_name:  Logic::EmptyOperator,
              value: empty.to_json
            },
            {
              targeted_champ: empty.to_json,
              operator_name:  Logic::EmptyOperator,
              value: empty.to_json
            }
          ]
        }
      end
      let(:initial_condition) do
        ds_and([
          empty_operator(empty, empty),
          empty_operator(empty, empty)
        ])
      end

      subject { delete :delete_row, params: default_params.merge(row_index: 0, procedure_revision: { condition_form: }), format: :turbo_stream }
      it 'remove condition' do
        procedure.draft_revision.update(ineligibilite_rules: initial_condition)

        expect { subject }
          .to change { procedure.draft_revision.reload.ineligibilite_rules }
          .from(initial_condition)
          .to(empty_operator(empty, empty))
      end
    end

    context 'simple tdc' do
      let(:types_de_champ_public) { [{ type: :yes_no }] }
      let(:yes_no_tdc) { procedure.draft_revision.types_de_champ_for(scope: :public).first }
      let(:targeted_champ) { champ_value(yes_no_tdc.stable_id).to_json }

      describe '#change_targeted_champ' do
        let(:condition_form) do
          {
            rows: [
              {
                targeted_champ: targeted_champ,
                operator_name:  Logic::Eq.name,
                value: constant(true).to_json
              }
            ]
          }
        end
        subject { patch :change_targeted_champ, params: default_params.merge(procedure_revision: { condition_form: }), format: :turbo_stream }
        it 'update condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(nil)
            .to(ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)))
        end
      end

      describe '#update' do
        let(:value) { constant(true).to_json }
        let(:operator_name) { Logic::Eq.name }
        let(:condition_form) do
          {
            rows: [
              {
                targeted_champ: targeted_champ,
                operator_name: operator_name,
                value: value
              }
            ]
          }
        end
        subject { patch :update, params: default_params.merge(procedure_revision: { condition_form: condition_form }), format: :turbo_stream }
        it 'updates condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(nil)
            .to(ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)))
        end
      end
    end

    context 'repetition tdc' do
      let(:types_de_champ_public) { [{ type: :repetition, children: [{ type: :yes_no }] }] }
      let(:yes_no_tdc) { procedure.draft_revision.types_de_champ_for(scope: :public).find { _1.type_champ == 'yes_no' } }
      let(:targeted_champ) { champ_value(yes_no_tdc.stable_id).to_json }
      let(:condition_form) do
        {
          rows: [
            {
              targeted_champ: targeted_champ,
              operator_name:  Logic::Eq.name,
              value: constant(true).to_json
            }
          ]
        }
      end
      subject { patch :change_targeted_champ, params: default_params.merge(procedure_revision: { condition_form: }), format: :turbo_stream }
      describe "#update" do
        it 'update condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(nil)
            .to(ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)))
        end
      end

      describe '#change_targeted_champ' do
        let(:condition_form) do
          {
            rows: [
              {
                targeted_champ: targeted_champ,
                operator_name:  Logic::Eq.name,
                value: constant(true).to_json
              }
            ]
          }
        end
        subject { patch :change_targeted_champ, params: default_params.merge(procedure_revision: { condition_form: }), format: :turbo_stream }
        it 'update condition' do
          expect { subject }.to change { procedure.draft_revision.reload.ineligibilite_rules }
            .from(nil)
            .to(ds_eq(champ_value(yes_no_tdc.stable_id), constant(true)))
        end
      end
    end
  end

  describe '#edit' do
    subject { get :edit, params: { procedure_id: procedure.id } }

    context 'when user is not signed in' do
      it { is_expected.to redirect_to(new_user_session_path) }
    end

    context 'when user is signed in but not admin of procedure' do
      before { sign_in(user) }
      it { is_expected.to redirect_to(new_user_session_path) }
    end

    context 'when user is signed as admin' do
      before do
        sign_in(admin.user)
        subject
      end

      it { is_expected.to have_http_status(200) }

      context 'rendered without tdc' do
        let(:types_de_champ_public) { [] }
        render_views

        it { expect(response.body).to have_link("Ajouter un champ supportant les conditions d’inéligibilité") }
      end

      context 'rendered with tdc' do
        let(:types_de_champ_public) { [{ type: :yes_no }] }
        render_views

        it { expect(response.body).not_to have_link("Ajouter un champ supportant les conditions d’inéligibilité") }
      end
    end
  end

  describe 'change' do
    let(:params) do
      {
        procedure_id: procedure.id,
        procedure_revision: {
          ineligibilite_message: 'panpan',
          ineligibilite_enabled: '1'
        }
      }
    end
    before { sign_in(admin.user) }

    context 'when ineligibilite rules is empty' do
      it 'fails gracefull without ineligibilite rules' do
        patch :change, params: params
        draft_revision = procedure.reload.draft_revision
        expect(draft_revision.ineligibilite_enabled).to eq(false)
        expect(flash[:alert]).to include("Le champ « Les conditions d’inéligibilité » doit être rempli")
      end
    end

    context 'when ineligibilite rules is present' do
      let(:types_de_champ_public) { [{ type: :drop_down_list, stable_id: 1, options: ['opt'] }] }
      before do
        procedure.draft_revision.update(ineligibilite_rules: ds_eq(champ_value(1), constant('opt')))
      end

      it 'works' do
        patch :change, params: params
        draft_revision = procedure.reload.draft_revision
        expect(draft_revision.ineligibilite_message).to eq('panpan')
        expect(draft_revision.ineligibilite_enabled).to eq(true)
        expect(response).to redirect_to(edit_admin_procedure_ineligibilite_rules_path(procedure))
      end
    end
  end
end