export const CURRENT_RELAY_PROTOCOL_VERSION = "2";
export const DEFAULT_RELAY_ENDPOINT = "relay.paseo.sh:443";
export function normalizeRelayProtocolVersion(value, fallback = CURRENT_RELAY_PROTOCOL_VERSION) {
    if (value == null) {
        return fallback;
    }
    let normalized = "";
    if (typeof value === "string") {
        normalized = value.trim();
    }
    else if (typeof value === "number") {
        normalized = String(value);
    }
    if (!normalized) {
        return fallback;
    }
    if (normalized === "1" || normalized === "2") {
        return normalized;
    }
    throw new Error('Relay version must be "1" or "2"');
}
function parsePort(portStr, context) {
    const port = Number(portStr);
    if (!Number.isInteger(port) || port < 1 || port > 65535) {
        throw new Error(`${context}: port must be between 1 and 65535`);
    }
    return port;
}
export function parseHostPort(input) {
    const trimmed = input.trim();
    if (!trimmed) {
        throw new Error("Host is required");
    }
    // IPv6: [::1]:6767
    if (trimmed.startsWith("[")) {
        const match = trimmed.match(/^\[([^\]]+)\]:(\d{1,5})$/);
        if (!match) {
            throw new Error("Invalid host:port (expected [::1]:6767)");
        }
        const host = match[1].trim();
        if (!host)
            throw new Error("Host is required");
        const port = parsePort(match[2], "Invalid host:port");
        return { host, port, isIpv6: true };
    }
    const match = trimmed.match(/^(.+):(\d{1,5})$/);
    if (!match) {
        throw new Error("Invalid host:port (expected localhost:6767)");
    }
    const host = match[1].trim();
    if (!host)
        throw new Error("Host is required");
    const port = parsePort(match[2], "Invalid host:port");
    return { host, port, isIpv6: false };
}
export function normalizeHostPort(input) {
    const { host, port, isIpv6 } = parseHostPort(input);
    return isIpv6 ? `[${host}]:${port}` : `${host}:${port}`;
}
export function parseConnectionUri(input) {
    const trimmed = input.trim();
    if (!trimmed) {
        throw new Error("Connection URI is required");
    }
    let parsed;
    try {
        parsed = new URL(trimmed);
    }
    catch {
        throw new Error("Invalid connection URI");
    }
    if (parsed.protocol !== "tcp:") {
        throw new Error("Connection URI protocol must be tcp:");
    }
    if (!parsed.hostname) {
        throw new Error("Connection URI host is required");
    }
    if (!parsed.port) {
        throw new Error("Connection URI port is required");
    }
    if (parsed.username || parsed.password) {
        throw new Error("Connection URI userinfo is not supported");
    }
    const isIpv6 = parsed.hostname.startsWith("[") && parsed.hostname.endsWith("]");
    const host = isIpv6 ? parsed.hostname.slice(1, -1) : parsed.hostname;
    const password = parsed.searchParams.get("password") || undefined;
    return {
        host,
        port: parsePort(parsed.port, "Invalid connection URI"),
        isIpv6,
        useTls: parsed.searchParams.get("ssl") === "true",
        ...(password ? { password } : {}),
    };
}
export function serializeConnectionUri(parts) {
    return createConnectionUri(parts).toString();
}
export function serializeConnectionUriForStorage(parts) {
    const url = createConnectionUri(parts);
    if (parts.password) {
        url.searchParams.set("password", parts.password);
    }
    return url.toString();
}
function createConnectionUri(parts) {
    const hostPart = parts.isIpv6 ? `[${parts.host}]` : parts.host;
    const url = new URL(`tcp://${hostPart}:${parts.port}`);
    if (parts.useTls) {
        url.searchParams.set("ssl", "true");
    }
    return url;
}
export function normalizeLoopbackToLocalhost(endpoint) {
    const { host, port, isIpv6 } = parseHostPort(endpoint);
    if (host === "127.0.0.1" || (!isIpv6 && host === "0.0.0.0")) {
        return `localhost:${port}`;
    }
    if (isIpv6 && (host === "::1" || host === "::")) {
        return `localhost:${port}`;
    }
    return endpoint;
}
export function deriveLabelFromEndpoint(endpoint) {
    try {
        const { host } = parseHostPort(endpoint);
        return host || "Unnamed Host";
    }
    catch {
        return "Unnamed Host";
    }
}
export function buildDaemonWebSocketUrl(endpoint, opts) {
    const { host, port, isIpv6 } = parseHostPort(endpoint);
    const protocol = opts.useTls ? "wss" : "ws";
    const hostPart = isIpv6 ? `[${host}]` : host;
    return new URL(`${protocol}://${hostPart}:${port}/ws`).toString();
}
export function buildRelayWebSocketUrl(params) {
    const { host, port, isIpv6 } = parseHostPort(params.endpoint);
    const protocol = params.useTls ? "wss" : "ws";
    const hostPart = isIpv6 ? `[${host}]` : host;
    const url = new URL(`${protocol}://${hostPart}:${port}/ws`);
    url.searchParams.set("serverId", params.serverId);
    url.searchParams.set("role", params.role);
    url.searchParams.set("v", normalizeRelayProtocolVersion(params.version));
    if (params.connectionId) {
        url.searchParams.set("connectionId", params.connectionId);
    }
    return url.toString();
}
/**
 * @deprecated Migration fallback for stored relay connections/offers that predate
 * explicit relay useTls. Delete after two release cycles.
 */
export function shouldUseTlsForDefaultHostedRelay(endpoint) {
    try {
        const { port } = parseHostPort(endpoint);
        return port === 443;
    }
    catch {
        return false;
    }
}
export function extractHostPortFromWebSocketUrl(wsUrl) {
    const parsed = new URL(wsUrl);
    if (parsed.protocol !== "ws:" && parsed.protocol !== "wss:") {
        throw new Error("Invalid WebSocket URL protocol");
    }
    if (parsed.pathname.replace(/\/+$/, "") !== "/ws") {
        throw new Error("Invalid WebSocket URL (expected /ws path)");
    }
    const host = parsed.hostname;
    let port;
    if (parsed.port) {
        port = Number(parsed.port);
    }
    else if (parsed.protocol === "wss:") {
        port = 443;
    }
    else {
        port = 80;
    }
    if (!host) {
        throw new Error("Invalid WebSocket URL (missing hostname)");
    }
    if (!Number.isInteger(port) || port < 1 || port > 65535) {
        throw new Error("Invalid WebSocket URL (invalid port)");
    }
    const isIpv6 = host.includes(":") && !host.startsWith("[") && !host.endsWith("]");
    return isIpv6 ? `[${host}]:${port}` : `${host}:${port}`;
}
export function isRelayClientWebSocketUrl(url) {
    try {
        const parsed = new URL(url);
        return parsed.searchParams.get("role") === "client" && parsed.searchParams.has("serverId");
    }
    catch {
        return false;
    }
}
//# sourceMappingURL=daemon-endpoints.js.map