import { buildSessionContext, SessionManager, } from "@sheason/pi-coding-agent";
import { assertWorkspaceInitialized, getWorkspacePaths } from "../workspace.js";
import { DEFAULT_AVAILABLE_THINKING_LEVELS } from "./session-options.js";
import { HUB_AGENT_SUMMARY_CUSTOM_TYPE, HUB_RUN_TIMING_CUSTOM_TYPE, } from "./session-snapshot.js";
export const DISABLED_SESSION_COMMAND_NAMES = ["new", "resume", "tree", "fork", "clone"];
const DISABLED_SESSION_COMMANDS = new Set(DISABLED_SESSION_COMMAND_NAMES);
export class UnsupportedSessionOperationError extends Error {
    constructor(commandName) {
        super(`Session operation "${commandName}" is disabled in pi-hub single-session mode.`);
    }
}
export class HubSessionService {
    sessionManager;
    sessionFile;
    now;
    seq = 0;
    isRunning = false;
    runStartedAt;
    lastRunStartedAt;
    lastRunEndedAt;
    lastRunDurationMs;
    lastRunEndReason;
    pendingToolCallIds = new Set();
    queuedMessages = [];
    steeringMessages = [];
    followUpMessages = [];
    lastError;
    diagnostics = [];
    summary;
    availableModels = [];
    availableThinkingLevels = [...DEFAULT_AVAILABLE_THINKING_LEVELS];
    boundAgentSession;
    listeners = new Set();
    constructor(sessionManager, sessionFile, options = {}) {
        this.sessionManager = sessionManager;
        this.sessionFile = sessionFile;
        this.now = options.now ?? (() => Date.now());
        this.hydrateLastRunTimingFromEntries();
        this.hydrateSummaryFromEntries();
    }
    static open(cwd = process.cwd(), options = {}) {
        const paths = assertWorkspaceInitialized(cwd);
        const sessionManager = SessionManager.open(paths.sessionFile, paths.workspaceDir, cwd);
        return new HubSessionService(sessionManager, paths.sessionFile, options);
    }
    static openAgent(cwd, sessionFile, options = {}) {
        const paths = assertWorkspaceInitialized(cwd);
        const sessionManager = SessionManager.open(sessionFile, paths.workspaceDir, cwd);
        return new HubSessionService(sessionManager, sessionFile, options);
    }
    static createIfMissing(cwd = process.cwd(), options = {}) {
        const paths = getWorkspacePaths(cwd);
        const sessionManager = SessionManager.open(paths.sessionFile, paths.workspaceDir, cwd);
        return new HubSessionService(sessionManager, paths.sessionFile, options);
    }
    getSessionManager() {
        return this.sessionManager;
    }
    getHeader() {
        const header = this.sessionManager.getHeader();
        if (!header) {
            throw new Error("Hub session is missing a session header.");
        }
        return header;
    }
    getEntries() {
        return this.sessionManager.getEntries();
    }
    getSnapshot() {
        return {
            header: this.getHeader(),
            sessionFile: this.sessionFile,
            entries: this.getEntries(),
            context: this.getContextSnapshot(),
            availableModels: [...this.availableModels],
            availableThinkingLevels: [...this.availableThinkingLevels],
            isRunning: this.isRunning,
            pendingToolCallIds: [...this.pendingToolCallIds],
            queuedMessages: this.queuedMessages.map((message) => ({ ...message })),
            steeringMessages: this.steeringMessages.map((message) => ({ ...message })),
            followUpMessages: this.followUpMessages.map((message) => ({ ...message })),
            contextUsage: this.boundAgentSession?.getContextUsage(),
            autoCompactionEnabled: this.boundAgentSession?.autoCompactionEnabled ?? true,
            sessionName: this.sessionManager.getSessionName?.(),
            lastError: this.lastError,
            diagnostics: [...this.diagnostics],
            summary: this.summary,
            runStartedAt: this.runStartedAt,
            lastRunStartedAt: this.lastRunStartedAt,
            lastRunEndedAt: this.lastRunEndedAt,
            lastRunDurationMs: this.lastRunDurationMs,
            lastRunEndReason: this.lastRunEndReason,
        };
    }
    subscribe(listener) {
        this.listeners.add(listener);
        return () => {
            this.listeners.delete(listener);
        };
    }
    bindAgentSession(session, options) {
        this.boundAgentSession = session;
        this.diagnostics = [...(options?.diagnostics ?? [])];
        return this.syncBoundAgentSession();
    }
    unbindAgentSession() {
        this.boundAgentSession = undefined;
        this.pendingToolCallIds.clear();
        this.isRunning = false;
        this.queuedMessages = [];
        this.steeringMessages = [];
        this.followUpMessages = [];
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    syncBoundAgentSession() {
        if (this.boundAgentSession) {
            this.isRunning = this.boundAgentSession.isStreaming;
            this.pendingToolCallIds.clear();
            for (const toolCallId of this.boundAgentSession.state.pendingToolCalls) {
                this.pendingToolCallIds.add(toolCallId);
            }
        }
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    assertOperationSupported(commandName) {
        if (DISABLED_SESSION_COMMANDS.has(commandName)) {
            throw new UnsupportedSessionOperationError(commandName);
        }
    }
    setRunState(isRunning, reason = "completed") {
        if (isRunning && !this.runStartedAt) {
            const startedAt = new Date(this.now()).toISOString();
            this.runStartedAt = startedAt;
            this.lastRunStartedAt = startedAt;
        }
        let runTiming;
        if (!isRunning && this.isRunning && this.runStartedAt) {
            const endedAtMs = this.now();
            const startedAtMs = Date.parse(this.runStartedAt);
            const endedAt = new Date(endedAtMs).toISOString();
            const durationMs = Math.max(0, endedAtMs - startedAtMs);
            this.lastRunEndedAt = endedAt;
            this.lastRunDurationMs = durationMs;
            this.lastRunEndReason = reason;
            runTiming = {
                startedAt: this.runStartedAt,
                endedAt,
                durationMs,
                endReason: reason,
            };
            this.sessionManager.appendCustomEntry(HUB_RUN_TIMING_CUSTOM_TYPE, runTiming);
            this.runStartedAt = undefined;
        }
        this.isRunning = isRunning;
        return this.emit({
            type: "run_state_changed",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
            isRunning,
            runStartedAt: this.runStartedAt,
            lastRunStartedAt: this.lastRunStartedAt,
            lastRunEndedAt: this.lastRunEndedAt,
            lastRunDurationMs: this.lastRunDurationMs,
            lastRunEndReason: this.lastRunEndReason,
            lastError: reason === "error" ? this.lastError : undefined,
            runTiming,
        });
    }
    setInputQueue(messages, steering, followUp) {
        this.queuedMessages = messages.map((message) => ({ ...message }));
        if (steering !== undefined) {
            this.steeringMessages = steering.map((m) => ({ ...m, queueMode: "steer" }));
        }
        if (followUp !== undefined) {
            this.followUpMessages = followUp.map((m) => ({ ...m, queueMode: "followUp" }));
        }
        return this.emitQueueChangedEvent();
    }
    getInputQueue() {
        return [...this.queuedMessages];
    }
    addPendingToolCall(toolCallId) {
        this.pendingToolCallIds.add(toolCallId);
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    removePendingToolCall(toolCallId) {
        this.pendingToolCallIds.delete(toolCallId);
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    clearError() {
        this.lastError = undefined;
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    recordError(message, options = {}) {
        this.lastError = message;
        if (this.isRunning && options.endRun !== false) {
            this.setRunState(false, "error");
        }
        return this.emit({
            type: "error",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
            message,
            endRun: options.endRun ?? true,
        });
    }
    updateDiagnostics(diagnostics) {
        this.diagnostics = [...diagnostics];
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    updateSummary(summary) {
        const normalized = summary.trim();
        this.summary = normalized.length > 0 ? normalized : undefined;
        this.sessionManager.appendCustomEntry(HUB_AGENT_SUMMARY_CUSTOM_TYPE, { summary: this.summary ?? null });
        this.sessionManager.flushPendingEntries();
        return this.emit({
            type: "summary_changed",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
            summary: this.summary,
        });
    }
    updateSessionOptions(options) {
        this.availableModels = [...options.availableModels];
        this.availableThinkingLevels = [...(options.availableThinkingLevels ?? DEFAULT_AVAILABLE_THINKING_LEVELS)];
        return this.emit({
            type: "snapshot_updated",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
        });
    }
    nextSeq() {
        this.seq += 1;
        return this.seq;
    }
    emitQueueChangedEvent() {
        return this.emit({
            type: "queue_changed",
            seq: this.nextSeq(),
            timestamp: new Date().toISOString(),
            messages: this.queuedMessages.map((message) => ({ ...message })),
            steering: this.steeringMessages.map((message) => ({ ...message })),
            followUp: this.followUpMessages.map((message) => ({ ...message })),
        });
    }
    getContextSnapshot() {
        if (!this.boundAgentSession) {
            return buildSessionContext(this.sessionManager.getBranch());
        }
        return {
            messages: [...this.boundAgentSession.messages],
            thinkingLevel: this.boundAgentSession.thinkingLevel,
            model: this.boundAgentSession.model
                ? {
                    provider: this.boundAgentSession.model.provider,
                    modelId: this.boundAgentSession.model.id,
                }
                : null,
        };
    }
    emit(event) {
        for (const listener of this.listeners) {
            listener(event);
        }
        return event;
    }
    hydrateLastRunTimingFromEntries() {
        const entries = this.sessionManager.getEntries();
        for (let i = entries.length - 1; i >= 0; i -= 1) {
            const entry = entries[i];
            if (entry?.type !== "custom" || entry.customType !== HUB_RUN_TIMING_CUSTOM_TYPE) {
                continue;
            }
            const timing = parseHubRunTiming(entry.data);
            if (!timing) {
                continue;
            }
            this.lastRunStartedAt = timing.startedAt;
            this.lastRunEndedAt = timing.endedAt;
            this.lastRunDurationMs = timing.durationMs;
            this.lastRunEndReason = timing.endReason;
            return;
        }
    }
    hydrateSummaryFromEntries() {
        const entries = this.sessionManager.getEntries();
        for (let i = entries.length - 1; i >= 0; i -= 1) {
            const entry = entries[i];
            if (entry?.type !== "custom" || entry.customType !== HUB_AGENT_SUMMARY_CUSTOM_TYPE) {
                continue;
            }
            const summary = parseHubAgentSummary(entry.data);
            this.summary = summary;
            return;
        }
    }
}
function isHubRunEndReason(value) {
    return value === "completed" || value === "interrupted" || value === "error";
}
function validIsoString(value) {
    return typeof value === "string" && !Number.isNaN(Date.parse(value));
}
function parseHubRunTiming(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const candidate = value;
    if (!validIsoString(candidate.startedAt) ||
        !validIsoString(candidate.endedAt) ||
        typeof candidate.durationMs !== "number" ||
        !Number.isFinite(candidate.durationMs) ||
        !isHubRunEndReason(candidate.endReason)) {
        return undefined;
    }
    return {
        startedAt: candidate.startedAt,
        endedAt: candidate.endedAt,
        durationMs: candidate.durationMs,
        endReason: candidate.endReason,
    };
}
function parseHubAgentSummary(value) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const candidate = value;
    return typeof candidate.summary === "string" && candidate.summary.trim().length > 0
        ? candidate.summary.trim()
        : undefined;
}
//# sourceMappingURL=hub-session-service.js.map