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

import { SymbolRegistry } from "../packages/registry/src/symbolRegistry";
import type { SymbolConfig, SymbolRuntimeState } from "../packages/registry/src/types";

function createConfig(overrides: Partial<SymbolConfig> = {}): SymbolConfig {
  return {
    symbol: overrides.symbol ?? "BTC",
    maxNotional: overrides.maxNotional ?? 100_000,
    maxBase: overrides.maxBase ?? 1,
    enabled: overrides.enabled,
    tags: overrides.tags,
    metadata: overrides.metadata
  };
}

function createState(overrides: Partial<SymbolRuntimeState> = {}): SymbolRuntimeState {
  return {
    config: overrides.config ?? createConfig({ symbol: "BTC" }),
    status: overrides.status ?? "inactive",
    updatedAt: overrides.updatedAt ?? Date.now(),
    reason: overrides.reason,
    allocation: overrides.allocation,
    score: overrides.score
  };
}

describe("SymbolRegistry", () => {
  it("registers symbols and emits lifecycle events", () => {
    const registry = new SymbolRegistry();
    const onRegister = vi.fn();
    registry.on("registered", onRegister);

    const state = registry.register(createConfig({ symbol: "ETH" }));

    expect(state.status).toBe("inactive");
    expect(registry.get("ETH")).toBe(state);
    expect(onRegister).toHaveBeenCalledTimes(1);
    expect(onRegister).toHaveBeenCalledWith(state);
  });

  it("applies enabled=false as disabled status", () => {
    const registry = new SymbolRegistry();
    const state = registry.register(createConfig({ symbol: "SOL", enabled: false }));
    expect(state.status).toBe("disabled");
  });

  it("updates configuration and tracks allocation/score", () => {
    const registry = new SymbolRegistry();
    registry.register(createConfig({ symbol: "BTC" }));

    const updated = registry.updateConfig("BTC", { maxNotional: 200_000 });
    expect(updated.config.maxNotional).toBe(200_000);

    const allocation = { notionalLimit: 50_000, baseLimit: 0.5 };
    const scored = registry.setAllocation("BTC", allocation);
    expect(scored.allocation).toEqual(allocation);

    registry.setScore("BTC", 0.8);
    expect(registry.get("BTC")?.score).toBeCloseTo(0.8);
  });

  it("changes status and emits statusChanged events", () => {
    const registry = new SymbolRegistry();
    const onStatus = vi.fn();
    registry.on("statusChanged", onStatus);

    registry.register(createConfig({ symbol: "BTC" }));
    const activated = registry.activate("BTC");
    expect(activated.status).toBe("active");

    registry.pause("BTC", "volatility");
    expect(registry.get("BTC")?.status).toBe("paused");
    expect(registry.get("BTC")?.reason).toBe("volatility");

    registry.disable("BTC", "maintenance");
    expect(registry.get("BTC")?.status).toBe("disabled");
    expect(onStatus).toHaveBeenCalledTimes(3);
  });

  it("lists states by status and removes symbols", () => {
    const registry = new SymbolRegistry();
    registry.register(createConfig({ symbol: "BTC" }));
    registry.register(createConfig({ symbol: "ETH" }));
    registry.activate("BTC");

    const active = registry.list("active");
    expect(active).toHaveLength(1);
    expect(active[0].config.symbol).toBe("BTC");

    const inactive = registry.list("inactive");
    expect(inactive).toHaveLength(1);
    expect(inactive[0].config.symbol).toBe("ETH");

    const onRemoved = vi.fn();
    registry.on("removed", onRemoved);
    registry.remove("ETH");
    expect(registry.get("ETH")).toBeUndefined();
    expect(onRemoved).toHaveBeenCalledWith({ symbol: "ETH" });
  });
});

