import { describe, it, expect } from "vitest";

import { RiskAllocator } from "../packages/registry/src/riskAllocator";
import type { SymbolRuntimeState } from "../packages/registry/src/types";

function state(params: {
  symbol: string;
  maxNotional: number;
  maxBase: number;
  score?: number;
  status?: SymbolRuntimeState["status"];
}): SymbolRuntimeState {
  return {
    config: {
      symbol: params.symbol,
      maxNotional: params.maxNotional,
      maxBase: params.maxBase
    },
    status: params.status ?? "active",
    updatedAt: Date.now(),
    score: params.score
  };
}

describe("RiskAllocator", () => {
  it("allocates proportionally by score across active symbols", () => {
    const allocator = new RiskAllocator({ totalNotional: 1_000_000, totalBase: 10 });
    const allocations = allocator.allocate([
      state({ symbol: "BTC", maxNotional: 1_000_000, maxBase: 10, score: 2 }),
      state({ symbol: "ETH", maxNotional: 800_000, maxBase: 8, score: 1 }),
      state({ symbol: "SOL", maxNotional: 500_000, maxBase: 5, score: 0.5 })
    ]);

    expect(allocations.size).toBe(3);

    const btc = allocations.get("BTC");
    const eth = allocations.get("ETH");
    const sol = allocations.get("SOL");
    expect(btc).toBeDefined();
    expect(eth).toBeDefined();
    expect(sol).toBeDefined();

    expect(btc!.notionalLimit).toBeCloseTo(571_428.571, 2);
    expect(btc!.baseLimit).toBeCloseTo(5.714, 3);
    expect(eth!.notionalLimit).toBeCloseTo(285_714.285, 2);
    expect(eth!.baseLimit).toBeCloseTo(2.857, 3);
    expect(sol!.notionalLimit).toBeCloseTo(142_857.142, 2);
    expect(sol!.baseLimit).toBeCloseTo(1.428, 2);
  });

  it("omits inactive or zero-score symbols and enforces per-symbol limits", () => {
    const allocator = new RiskAllocator({ totalNotional: 100_000, totalBase: 10 });
    const allocations = allocator.allocate([
      state({ symbol: "BTC", maxNotional: 40_000, maxBase: 4, score: 10 }),
      state({ symbol: "ETH", maxNotional: 80_000, maxBase: 8, score: 5, status: "paused" }),
      state({ symbol: "SOL", maxNotional: 10_000, maxBase: 1, score: 0 })
    ]);

    expect(allocations.size).toBe(1);
    expect(allocations.get("BTC")).toEqual({ notionalLimit: 40_000, baseLimit: 4 });
  });

  it("respects minimum thresholds configured in options", () => {
    const allocator = new RiskAllocator(
      { totalNotional: 100_000, totalBase: 10 },
      { minNotionalPerSymbol: 30_000, minBasePerSymbol: 3 }
    );
    const allocations = allocator.allocate([
      state({ symbol: "BTC", maxNotional: 25_000, maxBase: 5, score: 1 }),
      state({ symbol: "ETH", maxNotional: 80_000, maxBase: 8, score: 3 })
    ]);

    expect(allocations.get("BTC")).toEqual({ notionalLimit: 25_000, baseLimit: 3 });
    expect(allocations.get("ETH")).toEqual({ notionalLimit: 75_000, baseLimit: 7.5 });
  });
});
