/**
* Async version of computeSegmentRange for when computeSegmentId is async
*/
const computeSegmentRangeAsync = async (startTimeMs, endTimeMs, durationMs, rendition, computeSegmentId) => {
	const segments = [];
	const segmentDurationMs = rendition.segmentDurationMs || 1e3;
	const startSegmentIndex = Math.floor(startTimeMs / segmentDurationMs);
	const endSegmentIndex = Math.floor(Math.min(endTimeMs, durationMs) / segmentDurationMs);
	for (let i = startSegmentIndex; i <= endSegmentIndex; i++) {
		const timeMs = i * segmentDurationMs;
		if (timeMs < durationMs) {
			const segmentId = await computeSegmentId(timeMs, rendition);
			if (segmentId !== void 0) segments.push(segmentId);
		}
	}
	return segments.filter((id, index, arr) => arr.indexOf(id) === index);
};
/**
* Compute buffer queue based on current state and desired segments
* Pure function - determines what segments should be fetched
*/
const computeBufferQueue = (desiredSegments, activeRequests, cachedSegments) => {
	return desiredSegments.filter((segmentId) => !activeRequests.has(segmentId) && !cachedSegments.has(segmentId));
};
/**
* Core media buffering logic with explicit dependencies
* Generic implementation that works for both audio and video
*/
const manageMediaBuffer = async (seekTimeMs, config, currentState, durationMs, signal, deps) => {
	if (!config.enableBuffering) return currentState;
	const rendition = await deps.getRendition();
	const endTimeMs = seekTimeMs + config.bufferDurationMs;
	const desiredSegments = await computeSegmentRangeAsync(seekTimeMs, endTimeMs, durationMs, rendition, deps.computeSegmentId);
	const newQueue = computeBufferQueue(desiredSegments, currentState.activeRequests, currentState.cachedSegments);
	const segmentsToFetch = newQueue.slice(0, config.maxParallelFetches);
	const newActiveRequests = new Set(currentState.activeRequests);
	const newCachedSegments = new Set(currentState.cachedSegments);
	const startNextSegment = (remainingQueue) => {
		if (remainingQueue.length === 0 || signal.aborted) return;
		const availableSlots = config.maxParallelFetches - newActiveRequests.size;
		if (availableSlots <= 0) return;
		const nextSegmentId = remainingQueue[0];
		if (nextSegmentId === void 0) return;
		if (newActiveRequests.has(nextSegmentId) || newCachedSegments.has(nextSegmentId)) {
			startNextSegment(remainingQueue.slice(1));
			return;
		}
		newActiveRequests.add(nextSegmentId);
		deps.fetchSegment(nextSegmentId, rendition).then(() => {
			if (signal.aborted) return;
			newActiveRequests.delete(nextSegmentId);
			newCachedSegments.add(nextSegmentId);
			startNextSegment(remainingQueue.slice(1));
		}).catch((error) => {
			if (signal.aborted) return;
			newActiveRequests.delete(nextSegmentId);
			deps.logError(`Failed to fetch segment ${nextSegmentId}`, error);
			startNextSegment(remainingQueue.slice(1));
		});
	};
	for (const segmentId of segmentsToFetch) {
		if (signal.aborted) break;
		newActiveRequests.add(segmentId);
		deps.fetchSegment(segmentId, rendition).then(() => {
			if (signal.aborted) return;
			newActiveRequests.delete(segmentId);
			newCachedSegments.add(segmentId);
			if (config.enableContinuousBuffering ?? true) {
				const remainingQueue = newQueue.slice(segmentsToFetch.length);
				startNextSegment(remainingQueue);
			}
		}).catch((error) => {
			if (signal.aborted) return;
			newActiveRequests.delete(segmentId);
			deps.logError(`Failed to fetch segment ${segmentId}`, error);
			if (config.enableContinuousBuffering ?? true) {
				const remainingQueue = newQueue.slice(segmentsToFetch.length);
				startNextSegment(remainingQueue);
			}
		});
	}
	return {
		currentSeekTimeMs: seekTimeMs,
		activeRequests: newActiveRequests,
		cachedSegments: newCachedSegments,
		requestQueue: newQueue.slice(segmentsToFetch.length)
	};
};
export { manageMediaBuffer };
