# frozen_string_literal: true

require "webmock/rspec"

USAGE_FIXTURES_DIR = File.expand_path("../../../fixtures/responses", __dir__)

RSpec.describe Dispatch::Adapter::Claude, "#usage_report (fixture-based)" do
  let(:oauth_key) { "sk-ant-oat-test-token" }
  let(:base_url)  { "https://api.anthropic.com" }
  let(:usage_url) { "#{base_url}/api/oauth/usage" }
  let(:profile_url) { "#{base_url}/api/oauth/profile" }

  subject(:adapter) do
    described_class.new(
      model: "claude-sonnet-4-6",
      api_key: oauth_key,
      base_url: base_url,
      is_oauth: true
    )
  end

  before { WebMock.disable_net_connect! }
  after  { WebMock.reset! }

  def load_fixture(filename)
    JSON.parse(File.read(File.join(USAGE_FIXTURES_DIR, filename)))
  end

  def stub_usage(body)
    stub_request(:get, usage_url)
      .to_return(
        status: 200,
        body: JSON.generate(body),
        headers: { "Content-Type" => "application/json" }
      )
  end

  def stub_profile(body)
    stub_request(:get, profile_url)
      .to_return(
        status: 200,
        body: JSON.generate(body),
        headers: { "Content-Type" => "application/json" }
      )
  end

  # ── Scenario 1: full payload (4 buckets) via fixture ─────────────────────

  describe "full payload via oauth-usage-full.json" do
    before do
      stub_usage(load_fixture("oauth-usage-full.json"))
      stub_profile(load_fixture("oauth-profile.json"))
    end

    it "returns a UsageReport" do
      expect(adapter.usage_report).to be_a(Dispatch::Adapter::UsageReport)
    end

    it "has 4 UsageLimitEntry rows" do
      expect(adapter.usage_report.limits.size).to eq(4)
    end

    it "all rows are UsageLimitEntry objects" do
      expect(adapter.usage_report.limits).to all(be_a(Dispatch::Adapter::UsageLimitEntry))
    end

    it "seven_day_sonnet is :exhausted (100%)" do
      entry = adapter.usage_report.limits.find { |e| e.id == "anthropic:7d:sonnet" }
      expect(entry.status).to eq(:exhausted)
    end

    it "each window carries the correct duration_ms" do
      report = adapter.usage_report
      five_h = report.limits.find { |e| e.id == "anthropic:5h" }
      seven_d = report.limits.find { |e| e.id == "anthropic:7d" }
      expect(five_h.window.duration_ms).to eq(18_000_000)
      expect(seven_d.window.duration_ms).to eq(604_800_000)
    end
  end

  # ── Scenario 2: partial payload (only 2 buckets) via fixture ─────────────

  describe "partial payload via oauth-usage-partial.json" do
    before do
      stub_usage(load_fixture("oauth-usage-partial.json"))
      stub_profile(load_fixture("oauth-profile.json"))
    end

    it "returns a UsageReport (not nil)" do
      expect(adapter.usage_report).to be_a(Dispatch::Adapter::UsageReport)
    end

    it "has exactly 2 UsageLimitEntry rows" do
      expect(adapter.usage_report.limits.size).to eq(2)
    end

    it "includes five_hour entry" do
      entry_ids = adapter.usage_report.limits.map(&:id)
      expect(entry_ids).to include("anthropic:5h")
    end

    it "includes seven_day entry" do
      entry_ids = adapter.usage_report.limits.map(&:id)
      expect(entry_ids).to include("anthropic:7d")
    end

    it "does NOT include seven_day_opus or seven_day_sonnet entries" do
      entry_ids = adapter.usage_report.limits.map(&:id)
      expect(entry_ids).not_to include("anthropic:7d:opus", "anthropic:7d:sonnet")
    end

    it "five_hour status is :ok (33%)" do
      entry = adapter.usage_report.limits.find { |e| e.id == "anthropic:5h" }
      expect(entry.status).to eq(:ok)
    end

    it "seven_day status is :ok (71%)" do
      entry = adapter.usage_report.limits.find { |e| e.id == "anthropic:7d" }
      expect(entry.status).to eq(:ok)
    end

    it "five_hour window.duration_ms is 18_000_000" do
      entry = adapter.usage_report.limits.find { |e| e.id == "anthropic:5h" }
      expect(entry.window.duration_ms).to eq(18_000_000)
    end

    it "seven_day window.duration_ms is 604_800_000" do
      entry = adapter.usage_report.limits.find { |e| e.id == "anthropic:7d" }
      expect(entry.window.duration_ms).to eq(604_800_000)
    end

    it "metadata.email is populated from profile fixture" do
      expect(adapter.usage_report.metadata[:email]).to eq("user@example.com")
    end
  end

  # ── Scenario 3: empty payload → nil (via fixture reference) ──────────────

  describe "empty payload fixture scenario" do
    before { stub_usage({}) }

    it "returns nil when no recognised buckets are present" do
      expect(adapter.usage_report).to be_nil
    end
  end

  # ── Scenario 6: profile fallback via oauth-profile.json ──────────────────

  describe "profile fallback via oauth-profile.json" do
    before do
      stub_usage(load_fixture("oauth-usage-partial.json"))
      stub_profile(load_fixture("oauth-profile.json"))
    end

    it "populates metadata[:email] from oauth-profile.json" do
      expect(adapter.usage_report.metadata[:email]).to eq("user@example.com")
    end

    it "populates metadata[:account_id] from oauth-profile.json" do
      expect(adapter.usage_report.metadata[:account_id]).to eq("acct_abc123")
    end
  end

  # ── Scenario 7: status thresholds (95% and 100%) ─────────────────────────

  describe "status thresholds" do
    before do
      stub_profile("email" => "u@example.com", "account_id" => "acct_1")
    end

    [
      [95.0, :warning],
      [100.0, :exhausted]
    ].each do |utilization, expected_status|
      it "#{utilization}% → #{expected_status}" do
        stub_usage({ "five_hour" => { "utilization" => utilization, "resets_at" => nil } })
        entry = adapter.usage_report.limits.first
        expect(entry.status).to eq(expected_status)
      end
    end
  end
end
