/**
 * Self-refreshing arena local-model set.
 *
 * The set's contents are rebuilt from the provider registry on an interval so
 * a provider that was unreachable at coder startup is reclassified as local
 * once it comes back — without a boocoder restart. The Set instance is stable
 * (consumers hold a ReadonlySet reference); only its contents change.
 *
 * Merge semantics per refresh: a reachable provider replaces its own
 * contribution; an unreachable provider keeps its last-known contribution
 * (stale-but-local classification is safer than flipping to the cloud lane).
 * Bare wire ids are contributed only by the default provider — bare ids
 * resolve through defaultProvider at call time, so advertising another
 * machine's models as bare would route them to the wrong host.
 */
import { getLlamaProviders, formatModelRef } from './llama-providers.js';

interface LogLike {
  warn: (obj: unknown, msg: string) => void;
}

export interface LocalModelSetHandle {
  /** Stable Set instance — pass this to analyzer/battle-runner deps. */
  set: ReadonlySet<string>;
  /** Fetch every provider's live model list and rebuild the set contents. */
  refresh: () => Promise<void>;
  /** Start periodic refresh. */
  start: (intervalMs: number) => void;
  /** Stop periodic refresh. */
  stop: () => void;
}

export function createLocalModelSet(log: LogLike): LocalModelSetHandle {
  const set = new Set<string>();
  const contributions = new Map<string, Set<string>>();
  let timer: NodeJS.Timeout | null = null;

  async function refresh(): Promise<void> {
    const { providers, defaultProvider } = getLlamaProviders();
    await Promise.all(
      providers.map(async (p) => {
        try {
          const res = await fetch(`${p.baseUrl}/v1/models`, {
            signal: AbortSignal.timeout(10_000),
          });
          if (!res.ok) return;
          const parsed = (await res.json()) as { data?: Array<{ id: string }> };
          const contrib = new Set<string>();
          for (const m of parsed.data ?? []) {
            contrib.add(formatModelRef(p.id, m.id));
            // Bare ids resolve via defaultProvider — only it contributes them.
            if (p.id === defaultProvider) contrib.add(m.id);
          }
          contributions.set(p.id, contrib);
        } catch (err) {
          // Unreachable — keep the last-known contribution.
          log.warn(
            { provider: p.id, err: err instanceof Error ? err.message : String(err) },
            'arena-local-models: provider unreachable; keeping last-known model set',
          );
        }
      }),
    );
    set.clear();
    for (const contrib of contributions.values()) {
      for (const id of contrib) set.add(id);
    }
  }

  return {
    set,
    refresh,
    start(intervalMs: number) {
      if (timer) return;
      timer = setInterval(() => void refresh(), intervalMs);
      timer.unref?.();
    },
    stop() {
      if (timer) clearInterval(timer);
      timer = null;
    },
  };
}
