import { afterEach, describe, expect, it } from "vitest";
import { getChannelPlugin } from "../channels/plugins/registry.js";
import { createEmptyPluginRegistry } from "./registry-empty.js";
import {
  getActivePluginChannelRegistryVersion,
  getActivePluginRegistryVersion,
  getActivePluginChannelRegistry,
  pinActivePluginChannelRegistry,
  releasePinnedPluginChannelRegistry,
  requireActivePluginChannelRegistry,
  resetPluginRuntimeStateForTest,
  setActivePluginRegistry,
} from "./runtime.js";

describe("channel registry pinning", () => {
  afterEach(() => {
    resetPluginRuntimeStateForTest();
  });

  it("returns the active registry when not pinned", () => {
    const registry = createEmptyPluginRegistry();
    setActivePluginRegistry(registry);
    expect(getActivePluginChannelRegistry()).toBe(registry);
  });

  it("preserves pinned channel registry across setActivePluginRegistry calls", () => {
    const startup = createEmptyPluginRegistry();
    startup.channels = [{ plugin: { id: "slack" } }] as never;
    setActivePluginRegistry(startup);
    pinActivePluginChannelRegistry(startup);

    // A subsequent registry swap (e.g., config-schema load) must not evict channels.
    const replacement = createEmptyPluginRegistry();
    setActivePluginRegistry(replacement);

    expect(getActivePluginChannelRegistry()).toBe(startup);
    expect(getActivePluginChannelRegistry()!.channels).toHaveLength(1);
  });

  it("re-pin invalidates cached channel lookups", () => {
    const setup = createEmptyPluginRegistry();
    const setupPlugin = { id: "slack", meta: {} } as never;
    setup.channels = [{ plugin: setupPlugin }] as never;
    setActivePluginRegistry(setup);
    pinActivePluginChannelRegistry(setup);

    expect(getChannelPlugin("slack")).toBe(setupPlugin);

    const full = createEmptyPluginRegistry();
    const fullPlugin = { id: "slack", meta: {} } as never;
    full.channels = [{ plugin: fullPlugin }] as never;
    setActivePluginRegistry(full);

    expect(getChannelPlugin("slack")).toBe(setupPlugin);

    const activeVersionBeforeRepin = getActivePluginRegistryVersion();
    const channelVersionBeforeRepin = getActivePluginChannelRegistryVersion();
    pinActivePluginChannelRegistry(full);

    expect(getActivePluginRegistryVersion()).toBe(activeVersionBeforeRepin);
    expect(getActivePluginChannelRegistryVersion()).toBe(channelVersionBeforeRepin + 1);
    expect(getChannelPlugin("slack")).toBe(fullPlugin);
  });

  it("updates channel registry on swap when not pinned", () => {
    const first = createEmptyPluginRegistry();
    setActivePluginRegistry(first);
    expect(getActivePluginChannelRegistry()).toBe(first);

    const second = createEmptyPluginRegistry();
    setActivePluginRegistry(second);
    expect(getActivePluginChannelRegistry()).toBe(second);
  });

  it("release restores live-tracking behavior", () => {
    const startup = createEmptyPluginRegistry();
    setActivePluginRegistry(startup);
    pinActivePluginChannelRegistry(startup);

    const replacement = createEmptyPluginRegistry();
    setActivePluginRegistry(replacement);
    expect(getActivePluginChannelRegistry()).toBe(startup);

    releasePinnedPluginChannelRegistry(startup);
    // After release, the channel registry should follow the active registry.
    expect(getActivePluginChannelRegistry()).toBe(replacement);
  });

  it("release is a no-op when the pinned registry does not match", () => {
    const startup = createEmptyPluginRegistry();
    setActivePluginRegistry(startup);
    pinActivePluginChannelRegistry(startup);

    const unrelated = createEmptyPluginRegistry();
    releasePinnedPluginChannelRegistry(unrelated);

    // Pin is still held — unrelated release was ignored.
    const replacement = createEmptyPluginRegistry();
    setActivePluginRegistry(replacement);
    expect(getActivePluginChannelRegistry()).toBe(startup);
  });

  it("requireActivePluginChannelRegistry creates a registry when none exists", () => {
    resetPluginRuntimeStateForTest();
    const registry = requireActivePluginChannelRegistry();
    expect(registry).toBeDefined();
    expect(registry.channels).toEqual([]);
  });

  it("resetPluginRuntimeStateForTest clears channel pin", () => {
    const startup = createEmptyPluginRegistry();
    setActivePluginRegistry(startup);
    pinActivePluginChannelRegistry(startup);

    resetPluginRuntimeStateForTest();

    const fresh = createEmptyPluginRegistry();
    setActivePluginRegistry(fresh);
    expect(getActivePluginChannelRegistry()).toBe(fresh);
  });
});
