import { randomUUID } from "node:crypto";
export function createPiboSessionId() {
    return `ps_${randomUUID()}`;
}
export function createPiSessionId() {
    return randomUUID();
}
export function createPiboSession(input, now = new Date().toISOString()) {
    return {
        id: input.id ?? createPiboSessionId(),
        piSessionId: input.piSessionId ?? createPiSessionId(),
        channel: input.channel,
        kind: input.kind,
        profile: input.profile,
        ownerScope: input.ownerScope,
        parentId: input.parentId,
        originId: input.originId,
        workspace: input.workspace,
        title: input.title,
        metadata: input.metadata ?? {},
        createdAt: now,
        updatedAt: now,
    };
}
export class InMemoryPiboSessionStore {
    byId = new Map();
    byPiSessionId = new Map();
    get(id) {
        return this.byId.get(id);
    }
    list() {
        return this.sort([...this.byId.values()]);
    }
    create(input) {
        const session = createPiboSession(input);
        if (this.byId.has(session.id)) {
            throw new Error(`Pibo session "${session.id}" already exists`);
        }
        if (this.byPiSessionId.has(session.piSessionId)) {
            throw new Error(`Pi session "${session.piSessionId}" is already attached to a Pibo session`);
        }
        this.set(session);
        return session;
    }
    update(id, input) {
        const existing = this.byId.get(id);
        if (!existing)
            return undefined;
        if (input.piSessionId && input.piSessionId !== existing.piSessionId) {
            const attached = this.byPiSessionId.get(input.piSessionId);
            if (attached && attached.id !== id) {
                throw new Error(`Pi session "${input.piSessionId}" is already attached to Pibo session "${attached.id}"`);
            }
        }
        const updated = {
            ...existing,
            piSessionId: input.piSessionId ?? existing.piSessionId,
            profile: input.profile ?? existing.profile,
            ownerScope: input.ownerScope ?? existing.ownerScope,
            parentId: input.parentId === null ? undefined : input.parentId ?? existing.parentId,
            originId: input.originId === null ? undefined : input.originId ?? existing.originId,
            workspace: input.workspace === null ? undefined : input.workspace ?? existing.workspace,
            title: input.title === null ? undefined : input.title ?? existing.title,
            metadata: input.metadata ?? existing.metadata,
            updatedAt: new Date().toISOString(),
        };
        this.set(updated, existing.piSessionId);
        return updated;
    }
    delete(id) {
        const existing = this.byId.get(id);
        if (!existing)
            return false;
        this.byId.delete(id);
        this.byPiSessionId.delete(existing.piSessionId);
        return true;
    }
    find(input) {
        return this.sort([...this.byId.values()].filter((session) => matchesFindInput(session, input)));
    }
    set(session, previousPiSessionId) {
        this.byId.set(session.id, session);
        if (previousPiSessionId && previousPiSessionId !== session.piSessionId) {
            this.byPiSessionId.delete(previousPiSessionId);
        }
        this.byPiSessionId.set(session.piSessionId, session);
    }
    sort(sessions) {
        return sessions.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
    }
}
export function matchesFindInput(session, input) {
    if (input.ids && !input.ids.includes(session.id))
        return false;
    if (input.channel !== undefined && session.channel !== input.channel)
        return false;
    if (input.kind !== undefined && session.kind !== input.kind)
        return false;
    if (input.ownerScope !== undefined && session.ownerScope !== input.ownerScope)
        return false;
    if (input.parentId !== undefined) {
        if (input.parentId === null) {
            if (session.parentId !== undefined)
                return false;
        }
        else if (session.parentId !== input.parentId) {
            return false;
        }
    }
    if (input.originId !== undefined && session.originId !== input.originId)
        return false;
    if (input.profile !== undefined && session.profile !== input.profile)
        return false;
    if (input.metadata && !metadataMatches(session.metadata, input.metadata))
        return false;
    return true;
}
function metadataMatches(metadata, expected) {
    const actual = metadata ?? {};
    for (const [key, value] of Object.entries(expected)) {
        if (JSON.stringify(actual[key]) !== JSON.stringify(value))
            return false;
    }
    return true;
}
