import { homedir } from "node:os";
import { sep } from "node:path";
import { deriveAgentStateBucket, getWorkspaceStateBucketPriority, } from "../shared/agent-state-bucket.js";
import { SortablePager } from "./pagination/sortable-pager.js";
import { normalizeWorkspaceId } from "./workspace-registry-model.js";
const FETCH_WORKSPACES_SORT_KEYS = [
    "status_priority",
    "activity_at",
    "name",
    "project_id",
];
export function summarizeFetchWorkspacesEntries(entries) {
    const workspaces = Array.from(entries, (entry) => ({
        id: entry.id,
        projectId: entry.projectId,
        projectDisplayName: entry.projectDisplayName,
        name: entry.name,
        status: entry.status,
        workspaceKind: entry.workspaceKind,
        activityAt: entry.activityAt,
    }));
    const statusCounts = new Map();
    for (const workspace of workspaces) {
        statusCounts.set(workspace.status, (statusCounts.get(workspace.status) ?? 0) + 1);
    }
    return {
        count: workspaces.length,
        projectIds: [...new Set(workspaces.map((workspace) => workspace.projectId))],
        statusCounts: Object.fromEntries(statusCounts),
        workspaces,
    };
}
export class WorkspaceDirectory {
    constructor(deps) {
        this.deps = deps;
        this.archivingByWorkspaceId = new Map();
        this.pager = new SortablePager({
            validKeys: FETCH_WORKSPACES_SORT_KEYS,
            defaultSort: [{ key: "activity_at", direction: "desc" }],
            label: "fetch_workspaces",
            getId: (workspace) => workspace.id,
            getSortValue: (workspace, key) => {
                switch (key) {
                    case "status_priority":
                        return getWorkspaceStateBucketPriority(workspace.status);
                    case "activity_at":
                        return workspace.activityAt ? Date.parse(workspace.activityAt) : null;
                    case "name":
                        return workspace.name.toLocaleLowerCase();
                    case "project_id":
                        return workspace.projectId.toLocaleLowerCase();
                    default:
                        throw new Error("unreachable");
                }
            },
        });
    }
    markArchiving(workspaceIds, archivingAt) {
        for (const workspaceId of workspaceIds) {
            this.archivingByWorkspaceId.set(workspaceId, archivingAt);
        }
    }
    clearArchiving(workspaceIds) {
        for (const workspaceId of workspaceIds) {
            this.archivingByWorkspaceId.delete(workspaceId);
        }
    }
    async buildDescriptorMap(options) {
        const [agents, persistedWorkspaces, persistedProjects] = await Promise.all([
            this.deps.listAgentPayloads(),
            this.deps.workspaceRegistry.list(),
            this.deps.projectRegistry.list(),
        ]);
        const activeProjects = new Map(persistedProjects
            .filter((project) => !project.archivedAt)
            .map((project) => [project.projectId, project]));
        const archivedProjectIds = new Set(persistedProjects.filter((project) => project.archivedAt).map((project) => project.projectId));
        const activeRecords = persistedWorkspaces.filter((workspace) => !workspace.archivedAt && !archivedProjectIds.has(workspace.projectId));
        const descriptorsByWorkspaceId = new Map();
        const workspaceIds = options.workspaceIds ? new Set(options.workspaceIds) : null;
        const workspaceIdsByDirectory = new Map(activeRecords.map((workspace) => [normalizeWorkspaceId(workspace.cwd), workspace.workspaceId]));
        const includedWorkspaces = activeRecords.filter((workspace) => !workspaceIds || workspaceIds.has(workspace.workspaceId));
        const workspaceDescriptors = await Promise.all(includedWorkspaces.map((workspace) => this.deps.buildWorkspaceDescriptor({
            workspace,
            projectRecord: activeProjects.get(workspace.projectId) ?? null,
            includeGitData: options.includeGitData,
        })));
        for (let i = 0; i < includedWorkspaces.length; i += 1) {
            const workspaceId = includedWorkspaces[i].workspaceId;
            descriptorsByWorkspaceId.set(workspaceId, {
                ...workspaceDescriptors[i],
                archivingAt: this.archivingByWorkspaceId.get(workspaceId) ?? null,
            });
        }
        for (const agent of agents) {
            if (agent.archivedAt) {
                continue;
            }
            if (!this.deps.isProviderVisibleToClient(agent.provider)) {
                continue;
            }
            const workspaceId = workspaceIdsByDirectory.get(normalizeWorkspaceId(agent.cwd));
            if (workspaceId === undefined) {
                continue;
            }
            const existing = descriptorsByWorkspaceId.get(workspaceId);
            if (!existing) {
                continue;
            }
            const bucket = deriveAgentStateBucket({
                status: agent.status,
                pendingPermissionCount: agent.pendingPermissions?.length ?? 0,
                requiresAttention: agent.requiresAttention,
                attentionReason: agent.attentionReason ?? null,
            });
            if (getWorkspaceStateBucketPriority(bucket) < getWorkspaceStateBucketPriority(existing.status)) {
                existing.status = bucket;
            }
        }
        return descriptorsByWorkspaceId;
    }
    resolveRegisteredWorkspaceIdForCwd(cwd, workspaces) {
        const normalizedCwd = normalizeWorkspaceId(cwd);
        const exact = workspaces.find((workspace) => workspace.cwd === normalizedCwd);
        if (exact) {
            return exact.workspaceId;
        }
        const userHome = homedir();
        let bestMatch = null;
        for (const workspace of workspaces) {
            if (workspace.cwd === userHome)
                continue;
            if (workspace.archivedAt)
                continue;
            const prefix = workspace.cwd.endsWith(sep) ? workspace.cwd : `${workspace.cwd}${sep}`;
            if (!normalizedCwd.startsWith(prefix)) {
                continue;
            }
            if (!bestMatch || workspace.cwd.length > bestMatch.cwd.length) {
                bestMatch = workspace;
            }
        }
        return bestMatch?.workspaceId ?? normalizedCwd;
    }
    async listDescriptors() {
        return Array.from((await this.buildDescriptorMap({
            includeGitData: true,
        })).values());
    }
    matchesFilter(input) {
        const { workspace, filter } = input;
        if (!filter) {
            return true;
        }
        if (filter.projectId && filter.projectId.trim().length > 0) {
            if (workspace.projectId !== filter.projectId.trim()) {
                return false;
            }
        }
        if (filter.idPrefix && filter.idPrefix.trim().length > 0) {
            if (!workspace.id.startsWith(filter.idPrefix.trim())) {
                return false;
            }
        }
        if (filter.query && filter.query.trim().length > 0) {
            const query = filter.query.trim().toLocaleLowerCase();
            const haystacks = [workspace.name, workspace.projectId, workspace.id];
            if (!haystacks.some((value) => value.toLocaleLowerCase().includes(query))) {
                return false;
            }
        }
        return true;
    }
    async listFetchEntries(request) {
        const filter = request.filter;
        const sort = this.pager.normalizeSort(request.sort);
        let entries = await this.listDescriptors();
        const listedCount = entries.length;
        entries = entries.filter((workspace) => this.matchesFilter({ workspace, filter }));
        const filteredCount = entries.length;
        entries.sort((left, right) => this.pager.compare(left, right, sort));
        const cursorToken = request.page?.cursor;
        if (cursorToken) {
            const cursor = this.pager.decode(cursorToken, sort);
            entries = entries.filter((workspace) => this.pager.compareWithCursor(workspace, cursor, sort) > 0);
        }
        const limit = request.page?.limit ?? 200;
        const pagedEntries = entries.slice(0, limit);
        const hasMore = entries.length > limit;
        const nextCursor = hasMore && pagedEntries.length > 0
            ? this.pager.encode(pagedEntries[pagedEntries.length - 1], sort)
            : null;
        this.deps.logger.debug({
            requestId: request.requestId,
            filter: request.filter ?? null,
            sort,
            page: request.page ?? null,
            listedCount,
            filteredCount,
            returnedCount: pagedEntries.length,
            hasMore,
            nextCursor,
        }, "fetch_workspaces_entries_listed");
        return {
            entries: pagedEntries,
            pageInfo: {
                nextCursor,
                prevCursor: request.page?.cursor ?? null,
                hasMore,
            },
        };
    }
}
//# sourceMappingURL=workspace-directory.js.map