import path from "node:path";
import { schedulerBaseUrl as defaultSchedulerBaseUrl } from "../config.js";
import { getRuntimeAccessToken, loadRuntimeBinding, normalizeSchedulerBaseUrl } from "./runtime-binding.js";
const LAUNCH_BINDING_TTL_MS = 5 * 60 * 1000;
const launchBindings = [];
function normalizeWorkspacePath(workspacePath) {
    const raw = String(workspacePath || "").trim();
    if (!raw) {
        return "";
    }
    return path.resolve(raw).toLowerCase();
}
function normalizeOptionalUrl(value) {
    return String(value || "").trim();
}
function canonicalizeSchedulerHostname(hostname) {
    const normalized = hostname.toLowerCase();
    if (normalized === "localhost" || normalized === "::1" || normalized === "[::1]") {
        return "127.0.0.1";
    }
    return normalized;
}
function normalizeSchedulerOrigin(serverUrl) {
    const raw = normalizeSchedulerBaseUrl(serverUrl) || String(serverUrl || "").trim();
    if (!raw) {
        return "";
    }
    try {
        const url = new URL(raw);
        if (url.protocol === "ws:") {
            url.protocol = "http:";
        }
        else if (url.protocol === "wss:") {
            url.protocol = "https:";
        }
        else {
            url.protocol = url.protocol.toLowerCase();
        }
        url.hostname = canonicalizeSchedulerHostname(url.hostname);
        return url.origin;
    }
    catch {
        return raw.replace(/\/+$/, "").toLowerCase();
    }
}
function pruneExpiredBindings(now = Date.now()) {
    for (let index = launchBindings.length - 1; index >= 0; index -= 1) {
        const enqueuedAt = Date.parse(launchBindings[index]?.enqueuedAt || "");
        if (!Number.isFinite(enqueuedAt) || now - enqueuedAt > LAUNCH_BINDING_TTL_MS) {
            launchBindings.splice(index, 1);
        }
    }
}
function createBindingId(agentId) {
    return `${agentId}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
}
export function enqueueMcpLaunchBinding(input) {
    pruneExpiredBindings();
    const agentId = String(input.agentId || "").trim();
    const workspaceKey = normalizeWorkspacePath(input.workspacePath);
    if (!agentId || !workspaceKey) {
        return null;
    }
    const runtimeState = loadRuntimeBinding();
    const schedulerUrl = normalizeSchedulerBaseUrl(input.serverUrl || input.schedulerBaseUrl || runtimeState.schedulerBaseUrl || defaultSchedulerBaseUrl) || normalizeOptionalUrl(input.serverUrl || input.schedulerBaseUrl || runtimeState.schedulerBaseUrl || defaultSchedulerBaseUrl);
    const schedulerBindingBaseUrl = normalizeSchedulerBaseUrl(input.schedulerBaseUrl || runtimeState.schedulerBaseUrl || defaultSchedulerBaseUrl) || normalizeOptionalUrl(input.schedulerBaseUrl || runtimeState.schedulerBaseUrl || defaultSchedulerBaseUrl);
    const binding = {
        id: createBindingId(agentId),
        agentId,
        workspacePath: path.resolve(String(input.workspacePath)),
        serverUrl: schedulerUrl,
        mcpHttpUrl: normalizeOptionalUrl(input.mcpHttpUrl),
        runtimeAccessToken: normalizeOptionalUrl(input.runtimeAccessToken),
        userId: normalizeOptionalUrl(input.userId),
        schedulerBaseUrl: schedulerBindingBaseUrl,
        enqueuedAt: new Date().toISOString(),
    };
    launchBindings.push(binding);
    return binding;
}
export function claimMcpLaunchBinding(request) {
    pruneExpiredBindings();
    const requestedAgentId = String(request.agentId || "").trim();
    const workspaceKey = normalizeWorkspacePath(request.workspacePath);
    const serverKey = normalizeSchedulerOrigin(request.serverUrl);
    if (!workspaceKey) {
        return null;
    }
    const serverMatches = launchBindings
        .map((binding, index) => ({ binding, index }))
        .filter(({ binding }) => (!requestedAgentId || binding.agentId === requestedAgentId) &&
        normalizeWorkspacePath(binding.workspacePath) === workspaceKey &&
        serverKey &&
        normalizeSchedulerOrigin(binding.serverUrl || binding.schedulerBaseUrl) === serverKey);
    let bindingIndex = serverMatches.length === 1 ? serverMatches[0]?.index ?? -1 : -1;
    if (bindingIndex < 0) {
        const workspaceMatches = launchBindings
            .map((binding, index) => ({ binding, index }))
            .filter(({ binding }) => (!requestedAgentId || binding.agentId === requestedAgentId) &&
            normalizeWorkspacePath(binding.workspacePath) === workspaceKey);
        if (workspaceMatches.length === 1) {
            bindingIndex = workspaceMatches[0]?.index ?? -1;
        }
    }
    if (bindingIndex < 0) {
        return null;
    }
    const [binding] = launchBindings.splice(bindingIndex, 1);
    if (!binding) {
        return null;
    }
    const runtimeState = loadRuntimeBinding();
    const userId = binding.userId || runtimeState.userId;
    const schedulerBaseUrl = binding.schedulerBaseUrl || runtimeState.schedulerBaseUrl;
    const runtimeAccessToken = binding.runtimeAccessToken || getRuntimeAccessToken(userId, schedulerBaseUrl);
    return {
        ...binding,
        runtimeAccessToken,
        userId,
        schedulerBaseUrl,
    };
}
export function getMcpLaunchQueueSize(workspacePath) {
    pruneExpiredBindings();
    const workspaceKey = normalizeWorkspacePath(workspacePath);
    if (!workspaceKey) {
        return 0;
    }
    return launchBindings.filter((binding) => normalizeWorkspacePath(binding.workspacePath) === workspaceKey).length;
}
export function clearMcpLaunchBindingQueues() {
    launchBindings.splice(0, launchBindings.length);
}
