require 'rails_helper'
require 'models/application_model_examples'
require 'models/concerns/can_be_imported_examples'

RSpec.describe Ticket::State, type: :model do
  it_behaves_like 'ApplicationModel'
  it_behaves_like 'CanBeImported'

  describe 'Default state' do
    describe 'of whole table:' do
      it 'has seven records' do
        expect(described_class.pluck(:name))
          .to match_array(%w[closed merged new open pending\ close pending\ reminder removed])
      end
    end

    describe 'of "new" state:' do
      it 'is the sole #default_create state' do
        expect(described_class.where(default_create: true))
          .to match_array([described_class.find_by(name: 'new')])
      end
    end

    describe 'of "open" state:' do
      it 'is the sole #default_follow_up state' do
        expect(described_class.where(default_follow_up: true))
          .to match_array([described_class.find_by(name: 'open')])
      end
    end
  end

  describe 'Class methods:' do
    describe '.by_category' do
      it 'looks up states by category' do
        expect(described_class.by_category(:open))
          .to be_an(ActiveRecord::Relation)
          .and include(instance_of(described_class))
      end

      context 'with invalid category name' do
        it 'raises RuntimeError' do
          expect { described_class.by_category(:invalidcategoryname) }
            .to raise_error(RuntimeError)
        end
      end
    end
  end

  describe 'Attributes:' do
    describe '#default_create' do
      let!(:original_default) { described_class.find_by(default_create: true) }

      context 'for newly created record' do
        subject!(:state) { build(:ticket_state, default_create: default_create) }

        context 'when true' do
          let(:default_create) { true }

          it 'unsets previous default' do
            expect { state.save }
              .to change { original_default.reload.default_create }.to(false)
              .and not_change { described_class.where(default_create: true).count }
          end
        end

        context 'when false' do
          let(:default_create) { false }

          it 'does not alter existing default' do
            expect { state.save }
              .to not_change { described_class.find_by(default_create: true) }
              .and not_change { described_class.where(default_create: true).count }
          end
        end
      end

      context 'for existing record' do
        subject!(:state) { create(:ticket_state, default_create: default_create) }

        context 'when true' do
          let(:default_create) { true }

          context 'and updated to false' do
            it 'assigns Ticket::State.first as default' do
              expect { state.update(default_create: false) }
                .to change { described_class.first.default_create }.to(true)
                .and not_change { described_class.where(default_create: true).count }
            end
          end

          context 'and destroyed' do
            it 'assigns Ticket::State.first as default' do
              expect { state.destroy }
                .to change { described_class.first.default_create }.to(true)
                .and not_change { described_class.where(default_create: true).count }
            end
          end
        end

        context 'when false' do
          let(:default_create) { false }

          context 'and updated to true' do
            it 'unsets previous default' do
              expect { state.update(default_create: true) }
                .to change { original_default.reload.default_create }.to(false)
                .and not_change { described_class.where(default_create: true).count }
            end
          end

          context 'and destroyed' do
            it 'does not alter existing default' do
              expect { state.destroy }
                .to not_change { described_class.find_by(default_create: true) }
                .and not_change { described_class.where(default_create: true).count }
            end
          end
        end
      end
    end

    describe '#default_follow_up' do
      let!(:original_default) { described_class.find_by(default_follow_up: true) }

      context 'for newly created record' do
        subject!(:state) { build(:ticket_state, default_follow_up: default_follow_up) }

        context 'when true' do
          let(:default_follow_up) { true }

          it 'unsets previous default' do
            expect { state.save }
              .to change { original_default.reload.default_follow_up }.to(false)
              .and not_change { described_class.where(default_follow_up: true).count }
          end
        end

        context 'when false' do
          let(:default_follow_up) { false }

          it 'does not alter existing default' do
            expect { state.save }
              .to not_change { described_class.find_by(default_follow_up: true) }
              .and not_change { described_class.where(default_follow_up: true).count }
          end
        end
      end

      context 'for existing record' do
        subject!(:state) { create(:ticket_state, default_follow_up: default_follow_up) }

        context 'when true' do
          let(:default_follow_up) { true }

          context 'and updated to false' do
            it 'assigns Ticket::State.first as default' do
              expect { state.update(default_follow_up: false) }
                .to change { described_class.first.default_follow_up }.to(true)
                .and not_change { described_class.where(default_follow_up: true).count }
            end
          end

          context 'and destroyed' do
            it 'assigns Ticket::State.first as default' do
              expect { state.destroy }
                .to change { described_class.first.default_follow_up }.to(true)
                .and not_change { described_class.where(default_follow_up: true).count }
            end
          end
        end

        context 'when false' do
          let(:default_follow_up) { false }

          context 'and updated to true' do
            it 'unsets previous default' do
              expect { state.update(default_follow_up: true) }
                .to change { original_default.reload.default_follow_up }.to(false)
                .and not_change { described_class.where(default_follow_up: true).count }
            end
          end

          context 'and destroyed' do
            it 'does not alter existing default' do
              expect { state.destroy }
                .to not_change { described_class.find_by(default_follow_up: true) }
                .and not_change { described_class.where(default_follow_up: true).count }
            end
          end
        end
      end
    end
  end
end
