# frozen_string_literal: true
RSpec.describe LlmQuota do
  fab!(:group)
  fab!(:user)
  fab!(:llm_model)

  before { group.add(user) }

  describe ".check_quotas!" do
    it "returns true when user is nil" do
      expect(described_class.check_quotas!(llm_model, nil)).to be true
    end

    it "returns true when no quotas exist for the user's groups" do
      expect(described_class.check_quotas!(llm_model, user)).to be true
    end

    it "raises no error when within quota" do
      quota = Fabricate(:llm_quota, group: group, llm_model: llm_model)
      _usage =
        Fabricate(
          :llm_quota_usage,
          user: user,
          llm_quota: quota,
          input_tokens_used: quota.max_tokens - 100,
        )

      expect { described_class.check_quotas!(llm_model, user) }.not_to raise_error
    end

    it "raises error when usage exceeds token limit" do
      quota = Fabricate(:llm_quota, group: group, llm_model: llm_model, max_tokens: 1000)
      _usage = Fabricate(:llm_quota_usage, user: user, llm_quota: quota, input_tokens_used: 1100)

      expect { described_class.check_quotas!(llm_model, user) }.to raise_error(
        LlmQuotaUsage::QuotaExceededError,
      )
    end

    it "raises error when usage exceeds usage limit" do
      quota = Fabricate(:llm_quota, group: group, llm_model: llm_model, max_usages: 10)
      _usage = Fabricate(:llm_quota_usage, user: user, llm_quota: quota, usages: 11)

      expect { described_class.check_quotas!(llm_model, user) }.to raise_error(
        LlmQuotaUsage::QuotaExceededError,
      )
    end

    it "checks all quotas from user's groups" do
      group2 = Fabricate(:group)
      group2.add(user)

      quota1 = Fabricate(:llm_quota, group: group, llm_model: llm_model, max_tokens: 1000)
      quota2 = Fabricate(:llm_quota, group: group2, llm_model: llm_model, max_tokens: 500)

      described_class.log_usage(llm_model, user, 900, 0) # Should create usages for both quotas

      expect { described_class.check_quotas!(llm_model, user) }.not_to raise_error

      described_class.log_usage(llm_model, user, 101, 0) # This should push quota2 over its limit

      expect { described_class.check_quotas!(llm_model, user) }.to raise_error(
        LlmQuotaUsage::QuotaExceededError,
      )

      # Verify the usage was logged for both quotas
      expect(LlmQuotaUsage.find_by(llm_quota: quota1).total_tokens_used).to eq(1001)
      expect(LlmQuotaUsage.find_by(llm_quota: quota2).total_tokens_used).to eq(1001)
    end
  end

  describe ".log_usage" do
    it "does nothing when user is nil" do
      expect { described_class.log_usage(llm_model, nil, 100, 50) }.not_to change(
        LlmQuotaUsage,
        :count,
      )
    end

    it "creates usage records when none exist" do
      _quota = Fabricate(:llm_quota, group: group, llm_model: llm_model)

      expect { described_class.log_usage(llm_model, user, 100, 50) }.to change(
        LlmQuotaUsage,
        :count,
      ).by(1)

      usage = LlmQuotaUsage.last
      expect(usage.input_tokens_used).to eq(100)
      expect(usage.output_tokens_used).to eq(50)
      expect(usage.usages).to eq(1)
    end

    it "updates existing usage records" do
      quota = Fabricate(:llm_quota, group: group, llm_model: llm_model)
      usage =
        Fabricate(
          :llm_quota_usage,
          user: user,
          llm_quota: quota,
          input_tokens_used: 100,
          output_tokens_used: 50,
          usages: 1,
        )

      described_class.log_usage(llm_model, user, 50, 25)

      usage.reload
      expect(usage.input_tokens_used).to eq(150)
      expect(usage.output_tokens_used).to eq(75)
      expect(usage.usages).to eq(2)
    end

    it "logs usage for all quotas from user's groups" do
      group2 = Fabricate(:group)
      group2.add(user)

      _quota1 = Fabricate(:llm_quota, group: group, llm_model: llm_model)
      _quota2 = Fabricate(:llm_quota, group: group2, llm_model: llm_model)

      expect { described_class.log_usage(llm_model, user, 100, 50) }.to change(
        LlmQuotaUsage,
        :count,
      ).by(2)

      expect(LlmQuotaUsage.where(user: user).count).to eq(2)
    end
  end
end
