# typed: false
require "rails_helper"

RSpec.describe GlobalState, type: :model do
  describe "validations" do
    subject do
      build(
        :global_state,
        key: "test_key",
        value: "test_value",
        value_type: "string",
      )
    end

    it { should validate_presence_of(:key) }
    it { should validate_uniqueness_of(:key) }
    it { should validate_presence_of(:value) }
    it { should validate_presence_of(:value_type) }

    it do
      should define_enum_for(:value_type).with_values(
               string: 0,
               counter: 1,
               duration: 2,
               password: 3,
             ).with_prefix(:value_type)
    end

    context "when value_type is counter" do
      subject { build(:global_state, value_type: :counter) }

      it "is valid with a positive numeric value" do
        subject.value = "123"
        expect(subject).to be_valid
      end

      it "is valid with a negative numeric value" do
        subject.value = "-42"
        expect(subject).to be_valid
      end

      it "is valid with zero" do
        subject.value = "0"
        expect(subject).to be_valid
      end

      it "is invalid with a non-numeric value" do
        subject.value = "abc"
        expect(subject).not_to be_valid
        expect(subject.errors[:value]).to include(
          "must be a valid integer for counter type",
        )
      end
    end

    context "when value_type is duration" do
      subject { build(:global_state, value_type: :duration) }

      it "is valid with a proper duration format" do
        %w[30s 5m 2h 1d].each do |duration|
          subject.value = duration
          expect(subject).to be_valid
        end
      end

      it "is invalid with an improper duration format" do
        subject.value = "invalid"
        expect(subject).not_to be_valid
        expect(subject.errors[:value]).to include(
          "must be a valid duration (e.g., '30s', '5m', '2h', '1d')",
        )
      end
    end
  end

  describe "enum methods" do
    it "generates predicate methods with prefix" do
      state = build(:global_state, value_type: :password)
      expect(state.value_type_password?).to be true
      expect(state.value_type_string?).to be false
      expect(state.value_type_counter?).to be false
      expect(state.value_type_duration?).to be false
    end
  end

  describe "#display_value" do
    it "masks password values" do
      state =
        build(
          :global_state,
          key: "key_3",
          value: "secret123",
          value_type: :password,
        )
      expect(state.display_value).to eq("••••••••")
    end

    it "shows regular values for non-password types" do
      %i[string counter duration].each do |type|
        state =
          build(:global_state, key: "key_4", value: "test123", value_type: type)
        expect(state.display_value).to eq("test123")
      end
    end
  end

  describe ".get" do
    it "returns the value for the given key" do
      create(:global_state, key: "test_key", value: "test_value")
      expect(GlobalState.get("test_key")).to eq("test_value")
    end

    it "returns nil for non-existent key" do
      expect(GlobalState.get("non_existent")).to be_nil
    end
  end

  describe ".set" do
    it "creates a new record if key does not exist" do
      expect { GlobalState.set("new_key", "new_value") }.to change(
        GlobalState,
        :count,
      ).by(1)

      state = GlobalState.find_by(key: "new_key")
      expect(state.value).to eq("new_value")
    end

    it "updates existing record if key exists" do
      state = create(:global_state, key: "test_key", value: "old_value")

      expect { GlobalState.set("test_key", "new_value") }.not_to change(
        GlobalState,
        :count,
      )

      expect(state.reload.value).to eq("new_value")
    end
  end
end
