# frozen_string_literal: true

RSpec.describe Dispatch::Adapter::Claude::PricingTable do
  describe ".known_ids" do
    it "returns an Array of Strings" do
      expect(described_class.known_ids).to be_an(Array)
      expect(described_class.known_ids).to all(be_a(String))
    end

    it "includes the three required models" do
      ids = described_class.known_ids
      expect(ids).to include("claude-opus-4-7-20251018")
      expect(ids).to include("claude-sonnet-4-5-20250929")
      expect(ids).to include("claude-haiku-4-5-20251001")
    end
  end

  describe ".lookup" do
    context "with a known model id" do
      subject(:pricing) { described_class.lookup("claude-opus-4-7-20251018") }

      it "returns a ModelPricing" do
        expect(pricing).to be_a(Dispatch::Adapter::ModelPricing)
      end

      it "has non-zero input_per_mtok" do
        expect(pricing.input_per_mtok).to be > 0
      end

      it "has non-zero output_per_mtok" do
        expect(pricing.output_per_mtok).to be > 0
      end

      it "has non-zero cache_read_per_mtok" do
        expect(pricing.cache_read_per_mtok).to be > 0
      end

      it "has non-zero cache_write_per_mtok" do
        expect(pricing.cache_write_per_mtok).to be > 0
      end
    end

    context "with claude-sonnet-4-5-20250929" do
      subject(:pricing) { described_class.lookup("claude-sonnet-4-5-20250929") }

      it "returns a ModelPricing with correct rates" do
        expect(pricing).to be_a(Dispatch::Adapter::ModelPricing)
        expect(pricing.input_per_mtok).to eq(3.0)
        expect(pricing.output_per_mtok).to eq(15.0)
        expect(pricing.cache_read_per_mtok).to eq(0.3)
        expect(pricing.cache_write_per_mtok).to eq(3.75)
      end
    end

    context "with claude-haiku-4-5-20251001" do
      subject(:pricing) { described_class.lookup("claude-haiku-4-5-20251001") }

      it "returns a ModelPricing with non-zero rates" do
        expect(pricing).to be_a(Dispatch::Adapter::ModelPricing)
        expect(pricing.input_per_mtok).to be > 0
        expect(pricing.output_per_mtok).to be > 0
      end
    end

    context "with an unknown model id" do
      it "returns nil" do
        expect(described_class.lookup("does-not-exist")).to be_nil
      end

      it "returns nil for empty string" do
        expect(described_class.lookup("")).to be_nil
      end
    end
  end

  describe ".context_window" do
    it "returns an Integer for a known model" do
      expect(described_class.context_window("claude-opus-4-7-20251018")).to be_an(Integer)
    end

    it "returns a positive value" do
      expect(described_class.context_window("claude-opus-4-7-20251018")).to be > 0
    end

    it "returns nil for an unknown model" do
      expect(described_class.context_window("unknown-model")).to be_nil
    end
  end

  describe ".max_output_tokens" do
    it "returns an Integer for a known model" do
      expect(described_class.max_output_tokens("claude-sonnet-4-5-20250929")).to be_an(Integer)
    end

    it "returns a positive value" do
      expect(described_class.max_output_tokens("claude-sonnet-4-5-20250929")).to be > 0
    end

    it "returns nil for an unknown model" do
      expect(described_class.max_output_tokens("unknown-model")).to be_nil
    end
  end

  describe "Pricing.calculate round-trip" do
    it "produces a valid UsageCost when fed a ModelInfo built from the table" do
      model_id = "claude-sonnet-4-5-20250929"
      pricing  = described_class.lookup(model_id)

      model_info = Dispatch::Adapter::ModelInfo.new(
        id: model_id,
        name: "Claude Sonnet 4.5",
        max_context_tokens: described_class.context_window(model_id),
        supports_vision: true,
        supports_tool_use: true,
        supports_streaming: true,
        pricing: pricing
      )

      usage = Dispatch::Adapter::Usage.new(
        input_tokens: 1_000_000,
        output_tokens: 1_000_000,
        cache_read_tokens: 1_000_000,
        cache_creation_tokens: 1_000_000
      )

      cost = Dispatch::Adapter::Pricing.calculate(usage, model_info)

      # input: 1M * $3.0/M  = $3.0
      # output: 1M * $15.0/M = $15.0
      # cache_read: 1M * $0.3/M = $0.3
      # cache_write: 1M * $3.75/M = $3.75
      # total: $22.05
      expect(cost).to be_a(Dispatch::Adapter::UsageCost)
      expect(cost.input).to eq(3.0)
      expect(cost.output).to eq(15.0)
      expect(cost.cache_read).to eq(0.3)
      expect(cost.cache_write).to eq(3.75)
      expect(cost.total).to eq(22.05)
    end
  end
end
