# frozen_string_literal: true

require 'spec_helper'

RSpec.describe VariantOverride do
  let(:variant) { create(:variant) }
  let(:hub)     { create(:distributor_enterprise) }

  describe "scopes" do
    let(:hub1) { create(:distributor_enterprise) }
    let(:hub2) { create(:distributor_enterprise) }
    let!(:vo1) {
      create(:variant_override, hub: hub1, variant:, import_date: Time.zone.now.yesterday)
    }
    let!(:vo2) {
      create(:variant_override, hub: hub2, variant:, import_date: Time.zone.now)
    }
    let!(:vo3) {
      create(:variant_override, hub: hub1, variant:, permission_revoked_at: Time.zone.now)
    }

    it "ignores variant_overrides with revoked_permissions by default" do
      expect(VariantOverride.all).not_to include vo3
      expect(VariantOverride.unscoped).to include vo3
    end

    it "finds variant overrides for a set of hubs" do
      expect(VariantOverride.for_hubs([hub1, hub2])).to match_array [vo1, vo2]
    end

    it "fetches import dates for hubs in descending order" do
      import_dates = VariantOverride.distinct_import_dates.pluck :import_date

      expect(import_dates[0].to_i).to eq(vo2.import_date.to_i)
      expect(import_dates[1].to_i).to eq(vo1.import_date.to_i)
    end

    describe "fetching variant overrides indexed by variant" do
      it "gets indexed variant overrides for one hub" do
        expect(VariantOverride.indexed(hub1)).to eq( variant => vo1 )
        expect(VariantOverride.indexed(hub2)).to eq( variant => vo2 )
      end

      it "does not include overrides for soft-deleted variants" do
        variant.delete
        expect(VariantOverride.indexed(hub1)).to eq( nil => vo1 )
      end
    end
  end

  describe "validation" do
    describe "ensuring that on_demand and count_on_hand are compatible" do
      let(:variant_override) do
        build_stubbed(
          :variant_override,
          hub: build_stubbed(:distributor_enterprise),
          variant: build_stubbed(:variant),
          on_demand:,
          count_on_hand:
        )
      end

      context "when using producer stock settings" do
        let(:on_demand) { nil }

        context "when count_on_hand is blank" do
          let(:count_on_hand) { nil }

          it "is valid" do
            expect(variant_override).to be_valid
          end
        end

        context "when count_on_hand is set" do
          let(:count_on_hand) { 1 }

          it "is invalid" do
            expect(variant_override).not_to be_valid
            error_message = I18n.t("using_producer_stock_settings_but_count_on_hand_set",
                                   scope: [i18n_scope_for_error, "count_on_hand"])
            expect(variant_override.errors[:count_on_hand]).to eq([error_message])
          end
        end
      end

      context "when on demand" do
        let(:on_demand) { true }

        context "when count_on_hand is blank" do
          let(:count_on_hand) { nil }

          it "is valid" do
            expect(variant_override).to be_valid
          end
        end

        context "when count_on_hand is set" do
          let(:count_on_hand) { 1 }

          it "is valid" do
            expect(variant_override).to be_valid
          end
        end
      end

      context "when limited stock" do
        let(:on_demand) { false }

        context "when count_on_hand is blank" do
          let(:count_on_hand) { nil }

          it "is invalid" do
            expect(variant_override).not_to be_valid
            error_message = I18n.t("limited_stock_but_no_count_on_hand",
                                   scope: [i18n_scope_for_error, "count_on_hand"])
            expect(variant_override.errors[:count_on_hand]).to eq([error_message])
          end
        end

        context "when count_on_hand is set" do
          let(:count_on_hand) { 1 }

          it "is valid" do
            expect(variant_override).to be_valid
          end
        end
      end
    end
  end

  describe "delegated price" do
    let!(:variant_with_price) { create(:variant, price: 123.45) }
    let(:price_object) { variant_with_price.default_price }

    context "when variant is soft-deleted" do
      before do
        variant_with_price.destroy
      end

      it "soft-deletes the price" do
        expect(price_object.reload.deleted_at).not_to be_nil
      end

      it "can access the soft-deleted price" do
        expect(variant_with_price.reload.default_price).to eq price_object
        expect(variant_with_price.price).to eq 123.45
      end
    end
  end

  describe "with price" do
    let(:variant_override) do
      build_stubbed(
        :variant_override,
        variant: build_stubbed(:variant),
        hub: build_stubbed(:distributor_enterprise),
        price: 12.34
      )
    end

    it "returns the numeric price" do
      expect(variant_override.price).to eq(12.34)
    end
  end

  describe "with nil count on hand" do
    let(:variant_override) do
      build(
        :variant_override,
        variant: build_stubbed(:variant),
        hub: build_stubbed(:distributor_enterprise),
        count_on_hand: nil,
        on_demand: true
      )
    end

    describe "stock_overridden?" do
      it "returns true" do
        expect(variant_override.stock_overridden?).to be true
      end
    end

    describe "move_stock!" do
      it "silently logs an error" do
        expect {
          variant_override.move_stock!(5)
        }.to change {
          variant_override.count_on_hand
        }.from(nil).to(5)
      end
    end
  end

  describe "with count on hand" do
    let(:variant_override) do
      build_stubbed(
        :variant_override,
        variant: build_stubbed(:variant),
        hub: build_stubbed(:distributor_enterprise),
        count_on_hand: 12
      )
    end

    it "returns the numeric count on hand" do
      expect(variant_override.count_on_hand).to eq(12)
    end

    describe "stock_overridden?" do
      it "returns true" do
        expect(variant_override.stock_overridden?).to be true
      end
    end

    describe "move_stock!" do
      let(:variant_override) do
        create(
          :variant_override,
          variant:,
          hub:,
          count_on_hand: 12
        )
      end

      it "does nothing for quantity zero" do
        variant_override.move_stock!(0)
        expect(variant_override.reload.count_on_hand).to eq(12)
      end

      it "increments count_on_hand when quantity is negative" do
        variant_override.move_stock!(2)
        expect(variant_override.reload.count_on_hand).to eq(14)
      end

      it "decrements count_on_hand when quantity is negative" do
        variant_override.move_stock!(-2)
        expect(variant_override.reload.count_on_hand).to eq(10)
      end
    end
  end

  describe "checking default stock value is present" do
    it "returns true when a default stock level has been set" do
      vo = build_stubbed(
        :variant_override,
        variant: build_stubbed(:variant),
        hub: build_stubbed(:distributor_enterprise),
        count_on_hand: 12,
        default_stock: 20
      )
      expect(vo.default_stock?).to be true
    end

    it "returns false when the override has no default stock level" do
      vo = build_stubbed(
        :variant_override,
        variant: build_stubbed(:variant),
        hub: build_stubbed(:distributor_enterprise),
        count_on_hand: 12,
        default_stock: nil
      )
      expect(vo.default_stock?).to be false
    end
  end

  describe "resetting stock levels" do
    describe "forcing the on hand level to the value in the default_stock field" do
      it "succeeds for variant override that forces limited stock" do
        vo = create(:variant_override, variant:, hub:, count_on_hand: 12,
                                       default_stock: 20, resettable: true)
        vo.reset_stock!

        vo.reload
        expect(vo.on_demand).to eq(false)
        expect(vo.count_on_hand).to eq(20)
      end

      it "succeeds for variant override that forces unlimited stock" do
        vo = create(:variant_override, :on_demand, variant:, hub:, default_stock: 20,
                                                   resettable: true)
        vo.reset_stock!

        vo.reload
        expect(vo.on_demand).to eq(false)
        expect(vo.count_on_hand).to eq(20)
      end

      it "succeeds for variant override that uses producer stock settings" do
        vo = create(:variant_override, :use_producer_stock_settings, variant:, hub:,
                                                                     default_stock: 20,
                                                                     resettable: true)
        vo.reset_stock!

        vo.reload
        expect(vo.on_demand).to eq(false)
        expect(vo.count_on_hand).to eq(20)
      end
    end

    it "silently logs an error if the variant override doesn't have a default stock level" do
      vo = create(:variant_override, variant:, hub:, count_on_hand: 12,
                                     default_stock: nil, resettable: true)
      expect(Bugsnag).to receive(:notify)
      vo.reset_stock!
      expect(vo.reload.count_on_hand).to eq(12)
    end

    it "doesn't reset the level if the behaviour is disabled" do
      vo = create(:variant_override, variant:, hub:, count_on_hand: 12,
                                     default_stock: 10, resettable: false)
      vo.reset_stock!
      expect(vo.reload.count_on_hand).to eq(12)
    end
  end

  context "extends LocalizedNumber" do
    it_behaves_like "a model using the LocalizedNumber module", [:price]
  end

  def i18n_scope_for_error
    "activerecord.errors.models.variant_override"
  end
end
