import { escapeHTML } from "../../core/format.js";
import { RemoteRelayClient } from "../../peers/peer-client.js";
import { PeerStore } from "../../peers/peer-store.js";
import { remotePeerThreadSourceContextKey } from "./channel-peer-context.js";
import { renderSessionInfoHTML, renderSessionInfoPlain } from "./session-format.js";
export function selectedTargetPeerId(preferencesStore, contextKey) {
    const targetPeerId = preferencesStore.get(contextKey).targetPeerId ?? undefined;
    return targetPeerId || undefined;
}
export function selectedTargetPeerLabel(peerId) {
    const peer = new PeerStore().get(peerId);
    return peer ? `${peer.name} (${peer.id})` : peerId;
}
export function selectedTargetNodeLabel(preferencesStore, contextKey) {
    const peerId = selectedTargetPeerId(preferencesStore, contextKey);
    return peerId ? selectedTargetPeerLabel(peerId) : "Local node";
}
export async function renderTargetPeerSession(options) {
    const targetPeerId = selectedTargetPeerId(options.preferencesStore, options.contextKey);
    if (!targetPeerId) {
        return null;
    }
    assertTargetPeerAllowed(options, targetPeerId);
    const snapshot = await targetPeerSnapshot(options, targetPeerId);
    const info = snapshot.session;
    return {
        plain: [`Remote peer session: ${selectedTargetPeerLabel(targetPeerId)}`, renderSessionInfoPlain(info)].join("\n"),
        html: [
            `<b>Remote peer session:</b> <code>${escapeHTML(selectedTargetPeerLabel(targetPeerId))}</code>`,
            renderSessionInfoHTML(info),
        ].join("\n"),
    };
}
export async function listTargetPeerSessions(options) {
    const targetPeerId = selectedTargetPeerId(options.preferencesStore, options.contextKey);
    if (!targetPeerId) {
        return null;
    }
    assertTargetPeerAllowed(options, targetPeerId);
    const snapshot = await targetPeerSnapshot(options, targetPeerId);
    const page = await peerProxy(options, targetPeerId, {
        method: "GET",
        path: "/api/sessions",
        query: {
            page: 1,
            limit: options.limit ?? 50,
            query: options.query ?? "",
            agent: snapshot.session.agentId,
        },
    });
    return {
        peerId: targetPeerId,
        peerLabel: selectedTargetPeerLabel(targetPeerId),
        sessions: parseSessionPage(page),
        activeThreadId: snapshot.session.threadId,
        agentId: snapshot.session.agentId,
        agentLabel: snapshot.session.agentLabel,
    };
}
export async function switchTargetPeerSession(options) {
    const targetPeerId = selectedTargetPeerId(options.preferencesStore, options.contextKey);
    if (!targetPeerId) {
        return null;
    }
    assertTargetPeerAllowed(options, targetPeerId);
    const result = await peerProxy(options, targetPeerId, {
        method: "POST",
        path: "/api/sessions/switch",
        body: { threadId: options.threadId },
    }, remotePeerThreadSourceContextKey(options.contextKey, options.threadId));
    const info = parseSessionInfoResult(result);
    options.preferencesStore.update(options.contextKey, {
        targetPeerId,
        targetThreadId: info.threadId ?? options.threadId,
        targetAgentId: info.agentId ?? null,
    });
    return {
        peerId: targetPeerId,
        peerLabel: selectedTargetPeerLabel(targetPeerId),
        info,
    };
}
export async function renderTargetPeerMirrorPreference(options) {
    const targetPeerId = selectedTargetPeerId(options.preferencesStore, options.contextKey);
    if (!targetPeerId) {
        return null;
    }
    assertTargetPeerAllowed(options, targetPeerId);
    const argument = options.argument.trim();
    const result = await peerProxy(options, targetPeerId, {
        method: argument ? "POST" : "GET",
        path: "/api/chat/mirror",
        body: argument ? { argument } : undefined,
    });
    const parsed = parseMirrorResult(result);
    const peerLabel = selectedTargetPeerLabel(targetPeerId);
    options.preferencesStore.update(options.contextKey, { mirrorMode: parsed.mode });
    return {
        mode: parsed.mode,
        peerId: targetPeerId,
        peerLabel,
        response: {
            plain: [`Remote peer: ${peerLabel}`, parsed.response.plain].join("\n"),
            html: [`<b>Remote peer:</b> <code>${escapeHTML(peerLabel)}</code>`, parsed.response.html].join("\n"),
        },
    };
}
export function remoteSessionChoiceValue(peerId, threadId) {
    return `peer:${peerId}:${threadId}`;
}
export function parseRemoteSessionChoice(value) {
    const match = /^peer:([^:]+):(.+)$/.exec(value);
    if (!match?.[1] || !match[2]) {
        return null;
    }
    return {
        peerId: match[1],
        threadId: match[2],
    };
}
async function targetPeerSnapshot(options, peerId) {
    const snapshot = await peerProxy(options, peerId, { method: "GET", path: "/api/snapshot" });
    if (!snapshot || typeof snapshot !== "object" || !("session" in snapshot)) {
        throw new Error("Remote peer did not return a session snapshot.");
    }
    const session = snapshot.session;
    if (!session || typeof session !== "object") {
        throw new Error("Remote peer returned an invalid session snapshot.");
    }
    return { session: session };
}
async function peerProxy(options, peerId, payload, sourceContextKey = selectedTargetPeerSourceContextKey(options)) {
    assertTargetPeerAllowed(options, peerId);
    const client = options.remoteClient ?? new RemoteRelayClient();
    return client.webProxy(peerId, payload, options.actor, sourceContextKey);
}
export function selectedTargetPeerSourceContextKey(options) {
    const preferences = options.preferencesStore.get(options.contextKey);
    return remotePeerThreadSourceContextKey(options.contextKey, preferences.targetThreadId);
}
function assertTargetPeerAllowed(options, peerId) {
    if (options.canUsePeer && !options.canUsePeer(peerId)) {
        throw new Error(`Access denied for peer target: ${selectedTargetPeerLabel(peerId)}.`);
    }
}
function parseSessionPage(value) {
    if (!value || typeof value !== "object") {
        return [];
    }
    const sessions = value.sessions;
    return Array.isArray(sessions) ? sessions.filter(isAgentThreadRecord).map(normalizeAgentThreadRecord) : [];
}
function parseSessionInfoResult(value) {
    if (!value || typeof value !== "object") {
        throw new Error("Remote peer returned an invalid session response.");
    }
    const session = value.session;
    if (!session || typeof session !== "object") {
        throw new Error("Remote peer returned an invalid session response.");
    }
    return session;
}
function parseMirrorResult(value) {
    if (!value || typeof value !== "object") {
        throw new Error("Remote peer returned an invalid mirror response.");
    }
    const record = value;
    if (record.mode !== "off" && record.mode !== "status" && record.mode !== "final" && record.mode !== "full") {
        throw new Error("Remote peer returned an invalid mirror mode.");
    }
    return {
        mode: record.mode,
        minInterval: typeof record.minInterval === "number" ? record.minInterval : 0,
        response: {
            plain: typeof record.response?.plain === "string" ? record.response.plain : `CLI mirroring: ${record.mode}`,
            html: typeof record.response?.html === "string" ? record.response.html : `<b>CLI mirroring:</b> <code>${escapeHTML(record.mode)}</code>`,
        },
    };
}
function isAgentThreadRecord(value) {
    return Boolean(value && typeof value === "object" && typeof value.id === "string");
}
function normalizeAgentThreadRecord(record) {
    const updatedAt = normalizeDate(record.updatedAt) ?? new Date(0);
    const createdAt = normalizeDate(record.createdAt) ?? updatedAt;
    return {
        ...record,
        createdAt,
        updatedAt,
    };
}
function normalizeDate(value) {
    if (value instanceof Date && Number.isFinite(value.getTime())) {
        return value;
    }
    if (typeof value === "string" || typeof value === "number") {
        const parsed = new Date(value);
        if (Number.isFinite(parsed.getTime())) {
            return parsed;
        }
    }
    return null;
}
