import type { ObservationSourceDescriptor } from "../runtime/contracts";

export interface ObservationVisibilityChange {
  visibleIds: string[];
  removedIds: string[];
}

export function shouldRestartObservationSource(
  source: ObservationSourceDescriptor,
): boolean {
  return Boolean(
    source.activation?.selected &&
    source.activation.controllable &&
    (source.availability === "error" || !source.delivery),
  );
}

export function openObservationSource(
  currentIds: readonly string[],
  sourceId: string,
  sources: readonly ObservationSourceDescriptor[],
): ObservationVisibilityChange {
  const source = sources.find((candidate) => candidate.id === sourceId);
  if (!source) return { visibleIds: [...currentIds], removedIds: [] };

  const uniqueCurrent = [...new Set(currentIds)];
  const activation = source.activation;
  if (!activation) {
    return {
      visibleIds: uniqueCurrent.includes(sourceId) ? uniqueCurrent : [...uniqueCurrent, sourceId],
      removedIds: [],
    };
  }

  const groupSourceIds = new Set(
    sources
      .filter((candidate) => candidate.activation?.groupId === activation.groupId)
      .map((candidate) => candidate.id),
  );
  const capacity = Math.max(1, Math.floor(activation.maxActive));
  const currentPeers = uniqueCurrent.filter(
    (candidate) => candidate !== sourceId && groupSourceIds.has(candidate),
  );
  const retainedPeerCount = Math.max(0, capacity - 1);
  const retainedPeers = new Set(
    retainedPeerCount > 0 ? currentPeers.slice(-retainedPeerCount) : [],
  );
  const visibleIds = uniqueCurrent.filter(
    (candidate) => !groupSourceIds.has(candidate) || retainedPeers.has(candidate),
  );
  if (!visibleIds.includes(sourceId)) visibleIds.push(sourceId);
  const visibleSet = new Set(visibleIds);
  return {
    visibleIds,
    removedIds: uniqueCurrent.filter((candidate) => !visibleSet.has(candidate)),
  };
}

export function closeObservationSource(
  currentIds: readonly string[],
  sourceId: string,
): ObservationVisibilityChange {
  const visibleIds = currentIds.filter((candidate) => candidate !== sourceId);
  return {
    visibleIds,
    removedIds: visibleIds.length === currentIds.length ? [] : [sourceId],
  };
}
