import type { ServerDefinition } from '../config.js';
import { keepAliveIdleTimeout } from '../lifecycle.js';
import type { Runtime } from '../runtime.js';
import type { DaemonResponse } from './protocol.js';

export interface ServerActivity {
  connected: boolean;
  lastUsedAt?: number;
}

export function ensureManaged(server: string, managedServers: Map<string, ServerDefinition>): void {
  if (!managedServers.has(server)) {
    throw new Error(`Server '${server}' is not managed by the daemon.`);
  }
}

export function markActivity(server: string, activity: Map<string, ServerActivity>): void {
  const entry = activity.get(server);
  if (entry) {
    entry.connected = true;
    entry.lastUsedAt = Date.now();
  } else {
    activity.set(server, { connected: true, lastUsedAt: Date.now() });
  }
}

export async function evictIdleServers(
  runtime: Runtime,
  managedServers: Map<string, ServerDefinition>,
  activity: Map<string, ServerActivity>
): Promise<void> {
  const now = Date.now();
  await Promise.all(
    Array.from(managedServers.entries()).map(async ([name, definition]) => {
      const timeout = keepAliveIdleTimeout(definition);
      if (!timeout) {
        return;
      }
      const entry = activity.get(name);
      if (!entry?.lastUsedAt) {
        return;
      }
      if (now - entry.lastUsedAt < timeout) {
        return;
      }
      await runtime.close(name).catch(() => {});
      activity.set(name, { connected: false });
    })
  );
}

export function shouldShutdownDaemonForIdle(
  lastActivityAt: number,
  now: number,
  idleTimeoutMs: number | undefined,
  activeRequests = 0
): boolean {
  return (
    activeRequests <= 0 &&
    typeof idleTimeoutMs === 'number' &&
    idleTimeoutMs > 0 &&
    now - lastActivityAt >= idleTimeoutMs
  );
}

export function daemonIdleWatcherInterval(idleTimeoutMs: number | undefined): number {
  if (!idleTimeoutMs) {
    return 30_000;
  }
  return Math.min(30_000, Math.max(100, Math.floor(idleTimeoutMs / 2)));
}

export function buildErrorResponse(id: string, code: string, error?: unknown): DaemonResponse {
  let message = code;
  if (error instanceof Error) {
    message = error.message;
  } else if (typeof error === 'string') {
    message = error;
  }
  return {
    id,
    ok: false,
    error: {
      code,
      message,
    },
  };
}
