/**
 * Agent 客户端核心模块
 * 负责与调度中心通信，管理消息循环
 */
import { configManager } from "./config.js";
import { MESSAGE_POLL_INTERVAL_MS } from "./constants.js";
import { HttpClient } from "./http-client.js";
import { logger, setMcpNodeLogSink } from "./logger.js";
import { WebSocketClient } from "./websocket-client.js";
import { StatusReporter } from "./status-reporter.js";
import { claimRuntimeLaunchBinding } from "./runtime-launch-binding.js";
export class AgentClient {
    wsClient;
    httpClient;
    agentConfig;
    serverConfig;
    agentId = null;
    displayName = null;
    projectName = null; // Agent 所属项目（由服务端决定）
    isRunning = false;
    messageHandler = null;
    profileHandler = null;
    pollTimer = null;
    statusReporter = null;
    /**
     * Profile 缓存
     * 用于处理 PROFILE_INFO 消息在 profileHandler 设置之前到达的情况
     */
    cachedProfile = null;
    constructor() {
        configManager.loadConfig();
        this.agentConfig = configManager.getAgentConfig();
        this.serverConfig = configManager.getServerConfig();
        this.wsClient = new WebSocketClient(this.serverConfig);
        this.httpClient = new HttpClient(this.serverConfig);
        this.setupWebSocketListeners();
    }
    async initialize() {
        const binding = await claimRuntimeLaunchBinding(this.agentConfig, this.serverConfig);
        if (!binding) {
            return;
        }
        configManager.applyRuntimeLaunchBinding(binding);
        this.agentConfig = configManager.getAgentConfig();
        this.serverConfig = configManager.getServerConfig();
        this.wsClient = new WebSocketClient(this.serverConfig);
        this.httpClient = new HttpClient(this.serverConfig);
        this.setupWebSocketListeners();
    }
    // ============ WebSocket 事件监听 ============
    setupWebSocketListeners() {
        this.wsClient.on("directMessage", (message) => {
            this.handleIncomingDm(message);
        });
        this.wsClient.on("groupMessage", (message) => {
            this.handleIncomingGroup(message);
        });
        this.wsClient.on("profileInfo", (profile) => {
            logger.info(`[AgentClient] WebSocket profileInfo 事件触发，displayName: ${profile.displayName}`);
            this.handleProfileInfo(profile);
        });
        this.wsClient.on("agentStatus", (statusUpdate) => {
            logger.info(`[AgentClient] WebSocket agentStatus 事件触发: ${statusUpdate.status}`);
            // 状态更新由服务端推送，这里只记录日志
        });
        this.wsClient.on("disconnected", () => {
            logger.warn("[AgentClient] WebSocket 断开连接，正在自动重连");
        });
        this.wsClient.on("error", (error) => {
            logger.error("[AgentClient] WebSocket 错误:", error.message);
        });
        this.wsClient.on("reconnectFailed", () => {
            logger.error("[AgentClient] 重连失败，停止消息循环");
            this.stop();
        });
    }
    // ============ 注册相关 ============
    /**
     * 注册 Agent 上线
     */
    async register() {
        logger.info("[AgentClient] 正在注册 Agent...");
        if (this.hasPresetAgentId()) {
            return await this.registerWithExistingAgentId();
        }
        return await this.registerAsNewAgent();
    }
    hasPresetAgentId() {
        return !!(this.agentConfig.agentId && this.agentConfig.agentId.trim().length > 0);
    }
    /**
     * 使用预设 AgentID 注册（绑定已有实例）
     */
    async registerWithExistingAgentId() {
        const presetAgentId = this.agentConfig.agentId;
        if (!presetAgentId) {
            throw new Error("Preset agentId is missing");
        }
        this.agentId = presetAgentId.trim();
        const profile = await this.getProfileByAgentId(this.agentId);
        this.displayName = profile.displayName;
        this.projectName = profile.projectName || null; // 保存 Agent 所属项目
        logger.info(`[AgentClient] 使用预设 AgentID 绑定实例: ${this.displayName} (${this.agentId})`);
        logger.info(`[AgentClient] 所属项目: ${this.projectName || "(未设置)"}`);
        await this.wsClient.connect(this.agentId);
        logger.info("[AgentClient] WebSocket 连接已建立，Agent 现在已上线");
        // 初始化状态报告器
        this.initializeStatusReporter();
        // WebSocket 重新连上后，服务端会立刻把该 Agent 标记为在线。
        // 这里重新获取一次最新 profile，避免把连接前读取到的离线状态返回给调用方。
        const refreshedProfile = await this.getProfileByAgentId(this.agentId);
        this.displayName = refreshedProfile.displayName;
        this.projectName = refreshedProfile.projectName || null;
        return {
            agentId: this.agentId,
            displayName: this.displayName,
            status: refreshedProfile.isOnline ? "online" : "offline",
            projectName: refreshedProfile.projectName,
            workspacePath: refreshedProfile.workspacePath,
            runtimeStatus: refreshedProfile.runtimeStatus,
            runtimeSessionId: refreshedProfile.runtimeSessionId,
        };
    }
    /**
     * 作为新 Agent 注册
     */
    async registerAsNewAgent() {
        const result = await this.httpClient.callTool("register", {
            roleName: this.agentConfig.roleName,
            projectName: this.agentConfig.projectName,
            workspacePath: this.agentConfig.workspacePath,
            prompt: this.agentConfig.prompt,
            runtimeBridgeBaseUrl: this.serverConfig.runtimeBridgeBaseUrl,
        });
        const agentId = result.agentId;
        if (!agentId) {
            throw new Error("Registration failed: agentId was not returned");
        }
        this.agentId = agentId;
        this.displayName = result.displayName;
        this.projectName = result.projectName || null; // 保存服务端返回的项目名
        logger.info(`[AgentClient] 注册成功: ${this.displayName} (${this.agentId})`);
        logger.info(`[AgentClient] 状态: ${result.status}`);
        logger.info(`[AgentClient] 所属项目: ${this.projectName || "(未设置)"}`);
        await this.wsClient.connect(this.agentId);
        logger.info("[AgentClient] WebSocket 连接已建立，Agent 现在已上线");
        // 初始化状态报告器
        this.initializeStatusReporter();
        return result;
    }
    /**
     * 注销 Agent 下线
     */
    async unregister() {
        logger.info("[AgentClient] 正在注销 Agent...");
        if (this.agentId) {
            await this.httpClient.callTool("unregister", { agentId: this.agentId });
        }
        this.wsClient.disconnect();
        this.agentId = null;
        this.displayName = null;
        this.projectName = null;
        this.isRunning = false;
        logger.info("[AgentClient] 已注销");
    }
    // ============ 消息相关 ============
    /**
     * 获取当前 Agent 可见的同事列表（包含离线 Agent）。
     */
    async discoverColleagues() {
        return await this.httpClient.callTool("discover_colleague", {
            agentId: this.agentId,
        });
    }
    /**
     * 获取当前 Agent 在关系图中可发现的群组列表。
     */
    async discoverGroupRooms() {
        this.ensureRegistered();
        return await this.httpClient.callTool("discover_group_rooms", {
            agentId: this.agentId,
        });
    }
    /**
     * 招聘同事并创建新的 Agent 实例。
     * 主流程：校验当前实例已注册 -> 透传创建角色参数到服务端 MCP tool -> 返回新实例信息。
     */
    async hireColleague(args) {
        this.ensureRegistered();
        return await this.httpClient.callTool("hire_colleague", {
            agentId: this.agentId,
            ...args,
        });
    }
    /**
     * 批量发起招聘审批请求。
     * 主流程：校验当前实例已注册 -> 透传批量招聘参数到服务端 MCP tool -> 返回整批审批与创建结果。
     */
    async hireColleaguesBatch(args) {
        this.ensureRegistered();
        return await this.httpClient.callTool("hire_colleagues_batch", {
            agentId: this.agentId,
            ...args,
        });
    }
    /**
     * 发送群消息
     * @param content 消息内容
     * @param projectName 目标项目名，不传则使用 Agent 所属项目（由服务端决定）
     * @param roomId 目标群房间 ID，小组群场景下优先使用以避免误发到项目公共群
     */
    async sendGroupMessage(content, projectName, roomId) {
        this.ensureRegistered();
        // 优先级：传入参数 > Agent 所属项目（服务端决定） > 配置文件默认值
        const targetProject = projectName || this.projectName || this.agentConfig.projectName;
        return await this.httpClient.callTool("send_group_message", {
            agentId: this.agentId,
            projectName: targetProject,
            roomId,
            content,
        });
    }
    /**
     * 发送私信
     */
    async sendDirectMessage(targetId, content, waitReply = false, replyToCallId, timeoutMs) {
        this.ensureRegistered();
        const args = {
            agentId: this.agentId,
            targetId,
            content,
            requireReply: waitReply,
            waitReply,
            replyToCallId,
        };
        if (typeof timeoutMs === "number" && timeoutMs > 0) {
            args.timeoutMs = timeoutMs;
        }
        return await this.httpClient.callTool("send_dm", args);
    }
    /**
     * 发送私信并等待回复（阻塞式）
     */
    async sendCallAndWaitReply(targetId, content, timeoutMs = 60000) {
        const result = await this.sendDirectMessage(targetId, content, true, undefined, timeoutMs);
        if (result.reply) {
            return result.reply;
        }
        throw new Error("No reply received for call-style direct message before timeout");
    }
    /**
     * 回复调用式消息
     */
    async replyToCall(callId, targetId, content) {
        return await this.sendDirectMessage(targetId, content, false, callId);
    }
    /**
     * 获取私信历史消息
     */
    async getDmHistory(targetUserId, limit = 50, since) {
        this.ensureRegistered();
        const args = {
            agentId: this.agentId,
            targetUserId,
            limit,
        };
        if (since) {
            args.since = since;
        }
        return await this.httpClient.callTool("get_dm_history", args);
    }
    /**
     * 获取群消息历史记录
     */
    async getGroupHistory(limit = 50, roomId, since) {
        this.ensureRegistered();
        if (roomId) {
            const args = {
                projectName: this.projectName || this.agentConfig.projectName,
                roomId,
                limit,
            };
            if (since) {
                args.since = since;
            }
            return await this.httpClient.callTool("get_group_history", args);
        }
        const groups = await this.discoverGroupRooms();
        const targetProject = this.projectName || this.agentConfig.projectName;
        const roomIds = this.collectRoomIds(groups, targetProject);
        if (roomIds.length === 0) {
            return { messages: [], count: 0 };
        }
        const historyResponses = await Promise.all(roomIds.map(async (currentRoomId) => {
            const args = {
                projectName: targetProject,
                roomId: currentRoomId,
                limit,
            };
            if (since) {
                args.since = since;
            }
            return await this.httpClient.callTool("get_group_history", args);
        }));
        const messages = historyResponses
            .flatMap((response) => response.messages)
            .sort((left, right) => new Date(left.timestamp).getTime() -
            new Date(right.timestamp).getTime());
        return {
            messages,
            count: messages.length,
        };
    }
    /**
     * 拉取当前 Agent 尚未消费的未读消息。
     * 主流程：校验注册状态 -> 获取未读私信 -> 获取未读群消息 -> 合并返回。
     * @param since 可选的起始时间（ISO 8601），早于该时间的消息将被过滤
     */
    async fetchUnreadMessages(options = {}) {
        this.ensureRegistered();
        const targetProject = this.projectName || this.agentConfig.projectName;
        const dmArgs = {
            agentId: this.agentId,
            blockIfEmpty: options.blockIfEmpty ?? false,
        };
        if (options.since) {
            dmArgs.since = options.since;
        }
        const directMessagesResponse = await this.httpClient.callTool("get_dm_messages", dmArgs);
        const groups = await this.discoverGroupRooms();
        const roomIds = this.collectRoomIds(groups, targetProject);
        const groupMessageResponses = await Promise.all(roomIds.map(async (currentRoomId) => {
            const groupArgs = {
                agentId: this.agentId,
                projectName: targetProject,
                roomId: currentRoomId,
            };
            if (options.since) {
                groupArgs.since = options.since;
            }
            return await this.httpClient.callTool("get_group_messages", groupArgs);
        }));
        return {
            directMessages: directMessagesResponse.messages,
            groupMessages: groupMessageResponses
                .flatMap((response) => response.messages)
                .sort((left, right) => new Date(left.timestamp).getTime() -
                new Date(right.timestamp).getTime()),
        };
    }
    /**
     * 收集当前项目下 Agent 可访问的群房间 ID，避免离线补拉和历史查询遗漏小组群。
     */
    collectRoomIds(groups, projectName) {
        const roomIds = groups.groups
            .filter((group) => group.projectName === projectName)
            .map((group) => group.id);
        return Array.from(new Set(roomIds));
    }
    /**
     * 获取当前 Agent 的个人信息
     */
    async getProfile() {
        this.ensureRegistered();
        const agentId = this.agentId;
        if (!agentId) {
            throw new Error("Agent is not registered");
        }
        const profile = await this.getProfileByAgentId(agentId);
        return {
            ...profile,
            describe: this.agentConfig.describe,
        };
    }
    async getProfileByAgentId(agentId) {
        return await this.httpClient.callTool("get_profile", {
            agentId,
        });
    }
    // ============ 流程线任务相关 ============
    /**
     * 获取当前 Agent 被分配的流程线任务列表
     */
    async getMyTasks() {
        this.ensureRegistered();
        return await this.httpClient.callTool("my_task", {
            agentId: this.agentId,
        });
    }
    /**
     * 开始处理流程线任务节点（将 PENDING 状态转为 PROCESSING）
     */
    async startTask(nodeId) {
        this.ensureRegistered();
        return await this.httpClient.callTool("start_task", {
            agentId: this.agentId,
            nodeId,
        });
    }
    /**
     * 提交流程线任务结果
     */
    async submitTaskResult(nodeId, outputPayload) {
        this.ensureRegistered();
        return await this.httpClient.callTool("submit_task_result", {
            agentId: this.agentId,
            nodeId,
            outputPayload,
        });
    }
    /**
     * 拒绝流程线任务
     */
    async rejectTask(nodeId, reason) {
        this.ensureRegistered();
        return await this.httpClient.callTool("reject_task", {
            agentId: this.agentId,
            nodeId,
            reason,
        });
    }
    /**
     * 终止流程线任务（终止后不再流转）
     */
    async terminateTask(taskInstanceId, reason) {
        this.ensureRegistered();
        return await this.httpClient.callTool("terminate_task", {
            agentId: this.agentId,
            taskInstanceId,
            reason,
        });
    }
    // ============ 消息循环 ============
    /**
     * 启动消息监听
     */
    start(handler, options) {
        if (this.isRunning) {
            logger.warn("[AgentClient] 消息循环已在运行中");
            return;
        }
        this.ensureRegistered();
        this.messageHandler = handler;
        this.profileHandler = options?.profileHandler || null;
        this.isRunning = true;
        logger.info(`[AgentClient] start() 被调用，cachedProfile: ${this.cachedProfile ? "存在" : "不存在"}, profileHandler: ${this.profileHandler ? "已设置" : "未设置"}`);
        // 处理缓存的 profile 消息（在 profileHandler 设置之前到达的）
        if (this.cachedProfile && this.profileHandler) {
            logger.info(`[AgentClient] 处理缓存的 Profile 信息: ${this.cachedProfile.displayName}`);
            this.profileHandler(this.cachedProfile);
            this.cachedProfile = null;
        }
        const pollMode = options?.pollMode ?? false;
        if (pollMode) {
            logger.warn("[AgentClient] 启动消息主循环（HTTP轮询模式 - 不推荐）");
            this.messageLoop();
        }
        else {
            logger.info("[AgentClient] 启动消息主循环（WebSocket推送模式 - 推荐）");
        }
    }
    /**
     * 停止消息监听
     */
    stop() {
        this.isRunning = false;
        if (this.pollTimer) {
            clearTimeout(this.pollTimer);
            this.pollTimer = null;
        }
        logger.info("[AgentClient] 消息主循环已停止");
    }
    async messageLoop() {
        while (this.isRunning) {
            try {
                await this.delay(MESSAGE_POLL_INTERVAL_MS);
            }
            catch (error) {
                logger.error("[AgentClient] 消息循环错误:", error);
                await this.delay(5000);
            }
        }
    }
    // ============ WebSocket 消息处理 ============
    handleIncomingDm(message) {
        if (message.senderId === this.agentId) {
            return; // 过滤自己发送的消息
        }
        this.processMessage(message);
    }
    handleIncomingGroup(message) {
        if (message.senderId === this.agentId) {
            return; // 过滤自己发送的消息
        }
        this.processMessage(message);
    }
    /**
     * 处理收到的 Profile 信息
     * 由服务端在 Agent 连接时自动推送
     */
    handleProfileInfo(profile) {
        logger.info(`[AgentClient] 收到 Profile 信息: ${profile.displayName} (${profile.roleName})`);
        // 更新 Agent 所属项目（服务端决定的项目）
        if (profile.projectName) {
            this.projectName = profile.projectName;
            logger.info(`[AgentClient] 更新所属项目: ${this.projectName}`);
        }
        if (this.profileHandler) {
            // profileHandler 已设置，直接调用
            this.profileHandler(profile);
        }
        else {
            // profileHandler 未设置，缓存 profile
            logger.info("[AgentClient] profileHandler 未设置，缓存 Profile 信息");
            this.cachedProfile = profile;
        }
    }
    async processMessage(message) {
        if (!this.messageHandler) {
            return;
        }
        const isDm = typeof message.msgId === "string";
        logger.info(`[AgentClient] 收到${isDm ? "私信" : "群消息"}来自 ${message.senderName}: ${message.content.substring(0, 50)}...`);
        try {
            const reply = await this.messageHandler(message);
            // 处理调用式消息的回复
            if (isDm) {
                const dm = message;
                if (dm.requireReply && dm.callId) {
                    await this.replyToCall(dm.callId, dm.senderId, reply || "已收到消息，正在处理中...");
                }
            }
        }
        catch (error) {
            logger.error("[AgentClient] 处理消息失败:", error);
            // 处理调用式消息的错误回复
            if (typeof message.msgId === "string") {
                const dm = message;
                if (dm.requireReply && dm.callId) {
                    await this.replyToCall(dm.callId, dm.senderId, `处理失败: ${error}`);
                }
            }
        }
    }
    // ============ 工具方法 ============
    ensureRegistered() {
        if (!this.agentId) {
            throw new Error("Agent is not registered");
        }
    }
    delay(ms) {
        return new Promise((resolve) => {
            this.pollTimer = setTimeout(resolve, ms);
        });
    }
    getState() {
        return {
            agentId: this.agentId,
            displayName: this.displayName,
            projectName: this.projectName,
            isConnected: this.wsClient.isConnected(),
            isRunning: this.isRunning,
        };
    }
    getAgentConfig() {
        return this.agentConfig;
    }
    reloadConfig() {
        configManager.loadConfig();
        this.agentConfig = configManager.getAgentConfig();
        logger.info(`[AgentClient] 配置已重新加载，角色: ${this.agentConfig.roleName}`);
    }
    // ============ 状态报告 ============
    /**
     * 初始化状态报告器
     */
    initializeStatusReporter() {
        if (!this.agentId) {
            logger.warn("[AgentClient] 无法初始化状态报告器：agentId 为空");
            return;
        }
        this.statusReporter = new StatusReporter({
            agentId: this.agentId,
            sendStatusUpdate: (update) => {
                // 通过 WebSocket 发送状态更新
                this.wsClient.send({
                    type: "AGENT_STATUS",
                    data: update,
                    timestamp: new Date().toISOString(),
                });
            },
        });
        setMcpNodeLogSink((entry) => this.reportMcpNodeLog(entry));
        logger.info("[AgentClient] 状态报告器已初始化");
    }
    /**
     * 上报 MCP Node.js 进程自身日志。
     * 主流程：logger 写入 stderr/file -> sink 回调 -> WebSocket 转发给调度中心和面板。
     */
    reportMcpNodeLog(entry) {
        const maybeLogSender = this.wsClient;
        if (!this.agentId ||
            !this.wsClient.isConnected() ||
            typeof maybeLogSender.sendMcpNodeLog !== "function") {
            return;
        }
        maybeLogSender.sendMcpNodeLog(entry);
    }
    /**
     * 获取状态报告器（供 MCP Server 使用）
     */
    getStatusReporter() {
        return this.statusReporter;
    }
}
//# sourceMappingURL=agent-client.js.map