import fs from "node:fs";
import path from "node:path";
/**
 * 组织管理器 (OrgManager)
 * 负责处理跨 Agent 的通信、任务派发、状态汇报。
 * 它采用“信箱模式”(Mailbox Pattern)，通过在共享目录读写 JSON 文件实现解耦和高可靠的通信。
 */
export class OrgManager {
    config;
    heartbeatInterval = null;
    constructor(config) {
        this.config = config;
    }
    /** Get the primary department for this agent (used for registry/inbox) */
    getPrimaryDepartment() {
        const dept = this.config.org?.department;
        if (Array.isArray(dept))
            return dept[0] || "default";
        if (dept === "*")
            return "admin"; // Super boss's home
        return dept || "default";
    }
    /** Get all departments this agent can manage/view */
    getManagedDepartments() {
        const dept = this.config.org?.department;
        const root = this.config.org?.root;
        if (!root)
            return [];
        if (dept === "*") {
            const registryDir = path.join(root, "registry");
            if (!fs.existsSync(registryDir))
                return [];
            return fs.readdirSync(registryDir).filter(d => fs.statSync(path.join(registryDir, d)).isDirectory());
        }
        if (Array.isArray(dept))
            return dept;
        return [dept || "default"];
    }
    /**
     * 初始化组织环境
     * - 如果尚未注册，会在 registry 目录下创建该 Agent 的心跳文件。
     * - 如果是员工，会为其建立专属的收件箱文件夹。
     * - 启动定时器，定期更新自己的心跳状态（在线时间、忙闲状态），并清理过期历史任务。
     */
    async init() {
        if (!this.config.org)
            return;
        const { root } = this.config.org;
        const primaryDept = this.getPrimaryDepartment();
        const dirs = [
            path.join(root, "registry", primaryDept),
            path.join(root, "inbox", primaryDept, this.config.name),
        ];
        for (const dir of dirs) {
            fs.mkdirSync(dir, { recursive: true });
        }
        this.updateStatus("idle");
        // Start heartbeat
        this.heartbeatInterval = setInterval(() => {
            this.updateStatus();
            this.cleanup();
        }, 60000); // Every 1m
    }
    /**
     * 定期清理和归档逻辑
     * 1. 将收件箱里超过 24 小时的已完成/已失败任务转移到历史库 (history) 目录。
     * 2. 将卡在 running 状态超过 24 小时的死锁任务强行标记为 failed（防止 Agent 崩溃导致的僵尸任务）。
     * 3. 删除历史库中存放超过 7 天的文件，释放磁盘空间。
     */
    cleanup() {
        if (!this.config.org)
            return;
        const { root } = this.config.org;
        const primaryDept = this.getPrimaryDepartment();
        const inboxDir = path.join(root, "inbox", primaryDept, this.config.name);
        const historyDir = path.join(root, "history", primaryDept, this.config.name);
        if (!fs.existsSync(inboxDir))
            return;
        if (!fs.existsSync(historyDir))
            fs.mkdirSync(historyDir, { recursive: true });
        const now = Date.now();
        const ONE_DAY = 24 * 60 * 60 * 1000;
        const SEVEN_DAYS = 7 * ONE_DAY;
        // 1. Archive completed/failed tasks older than 24h, and fail stuck running tasks
        const files = fs.readdirSync(inboxDir).filter(f => f.endsWith(".json"));
        for (const f of files) {
            const p = path.join(inboxDir, f);
            try {
                const stats = fs.statSync(p);
                if (now - stats.mtimeMs > ONE_DAY) {
                    const task = JSON.parse(fs.readFileSync(p, "utf-8"));
                    if (task.status === "completed" || task.status === "failed") {
                        fs.renameSync(p, path.join(historyDir, f));
                    }
                    else if (task.status === "running") {
                        // Task has been running for > 24h, assume agent crashed
                        task.status = "failed";
                        task.result = "Task timed out (stuck in running state for > 24h).";
                        fs.writeFileSync(p, JSON.stringify(task, null, 2));
                        // It will be archived on the next cleanup run 24h later
                    }
                }
            }
            catch { }
        }
        // 2. Delete history older than 7 days
        if (fs.existsSync(historyDir)) {
            const historyFiles = fs.readdirSync(historyDir).filter(f => f.endsWith(".json"));
            for (const f of historyFiles) {
                const p = path.join(historyDir, f);
                try {
                    const stats = fs.statSync(p);
                    if (now - stats.mtimeMs > SEVEN_DAYS) {
                        fs.unlinkSync(p);
                    }
                }
                catch { }
            }
        }
    }
    updateStatus(status) {
        if (!this.config.org)
            return;
        const { root, role } = this.config.org;
        const primaryDept = this.getPrimaryDepartment();
        const registryPath = path.join(root, "registry", primaryDept, `${this.config.name}.json`);
        let currentStatus = status || "idle";
        try {
            if (fs.existsSync(registryPath) && !status) {
                const existing = JSON.parse(fs.readFileSync(registryPath, "utf-8"));
                currentStatus = existing.status;
            }
        }
        catch { }
        const agentStatus = {
            name: this.config.name,
            role,
            department: primaryDept,
            lastSeen: new Date().toISOString(),
            status: currentStatus,
        };
        fs.writeFileSync(registryPath, JSON.stringify(agentStatus, null, 2));
    }
    setStatus(status) {
        this.updateStatus(status);
    }
    /**
     * 获取团队名单 (仅老板常用)
     * 老板会扫描自己所管辖的部门注册表，读取所有员工的心跳文件。
     */
    listTeam() {
        if (!this.config.org)
            return [];
        const { root } = this.config.org;
        const depts = this.getManagedDepartments();
        const allMembers = [];
        for (const dept of depts) {
            const registryDir = path.join(root, "registry", dept);
            if (!fs.existsSync(registryDir))
                continue;
            const members = fs.readdirSync(registryDir)
                .filter(f => f.endsWith(".json"))
                .map(f => {
                try {
                    return JSON.parse(fs.readFileSync(path.join(registryDir, f), "utf-8"));
                }
                catch {
                    return null;
                }
            })
                .filter((s) => s !== null);
            allMembers.push(...members);
        }
        return allMembers;
    }
    /**
     * 指派任务 (核心方法)
     * @param target 目标员工名称，可以带前缀如 "dept:推广部"，或者填 "all" 广播全员。
     * @param instruction 具体的操作指令文本。
     * @returns 分发成功的任务 ID 列表。
     */
    assignTask(target, instruction) {
        if (!this.config.org || this.config.org.role !== "boss") {
            throw new Error("Only bosses can assign tasks.");
        }
        const { root } = this.config.org;
        const depts = this.getManagedDepartments();
        const targets = [];
        const allMembers = this.listTeam();
        if (target === "all") {
            targets.push(...allMembers.filter(m => m.role === "worker").map(m => ({ name: m.name, dept: m.department })));
        }
        else if (target.startsWith("dept:")) {
            const targetDept = target.replace("dept:", "");
            targets.push(...allMembers.filter(m => m.role === "worker" && m.department === targetDept).map(m => ({ name: m.name, dept: m.department })));
        }
        else {
            // Find specific worker across managed depts
            const worker = allMembers.find(m => m.name === target && m.role === "worker");
            if (worker) {
                targets.push({ name: worker.name, dept: worker.department });
            }
            else {
                throw new Error(`Worker '${target}' not found in your managed departments.`);
            }
        }
        const assignedTasks = [];
        for (const { name, dept } of targets) {
            const inboxDir = path.join(root, "inbox", dept, name);
            fs.mkdirSync(inboxDir, { recursive: true });
            const taskId = `task_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;
            const task = {
                id: taskId,
                instruction,
                assignedBy: this.config.name,
                assignedAt: new Date().toISOString(),
                status: "pending",
                targetDepartment: dept
            };
            fs.writeFileSync(path.join(inboxDir, `${taskId}.json`), JSON.stringify(task, null, 2));
            assignedTasks.push(`${dept}/${name}:${taskId}`);
        }
        return assignedTasks;
    }
    /**
     * 轮询查收新任务 (仅员工常用)
     * 员工读取自己收件箱下的所有 JSON 文件，筛选出状态为 pending 的任务。
     */
    pollTasks() {
        if (!this.config.org)
            return [];
        const { root } = this.config.org;
        const primaryDept = this.getPrimaryDepartment();
        const inboxDir = path.join(root, "inbox", primaryDept, this.config.name);
        if (!fs.existsSync(inboxDir))
            return [];
        const tasks = fs.readdirSync(inboxDir)
            .filter(f => f.endsWith(".json"))
            .map(f => {
            try {
                const p = path.join(inboxDir, f);
                const task = JSON.parse(fs.readFileSync(p, "utf-8"));
                if (task.status === "pending") {
                    return task;
                }
            }
            catch { }
            return null;
        })
            .filter((t) => t !== null);
        return tasks;
    }
    /**
     * 更新任务状态与回传结果
     * 员工在接单、完成、报错时，都会调用此方法覆盖修改收件箱里的同一个 JSON 文件。
     */
    updateTask(taskId, status, result) {
        if (!this.config.org)
            return;
        const { root } = this.config.org;
        const primaryDept = this.getPrimaryDepartment();
        const taskPath = path.join(root, "inbox", primaryDept, this.config.name, `${taskId}.json`);
        if (!fs.existsSync(taskPath))
            return;
        try {
            const task = JSON.parse(fs.readFileSync(taskPath, "utf-8"));
            task.status = status;
            if (result !== undefined)
                task.result = result;
            fs.writeFileSync(taskPath, JSON.stringify(task, null, 2));
        }
        catch { }
    }
    stop() {
        if (this.heartbeatInterval) {
            clearInterval(this.heartbeatInterval);
        }
    }
}
//# sourceMappingURL=org-manager.js.map