import { v4 as uuidv4 } from "uuid";
import * as fs from "fs/promises";
import * as path from "path";
import { logger } from "../monitoring/logger.js";
import { SystemError, ErrorCode } from "../errors/index.js";
function getEnv(key, defaultValue) {
  const value = process.env[key];
  if (value === void 0) {
    if (defaultValue !== void 0) return defaultValue;
    throw new Error(`Environment variable ${key} is required`);
  }
  return value;
}
function getOptionalEnv(key) {
  return process.env[key];
}
var FrameQueryMode = /* @__PURE__ */ ((FrameQueryMode2) => {
  FrameQueryMode2["CURRENT_SESSION"] = "current";
  FrameQueryMode2["PROJECT_ACTIVE"] = "project";
  FrameQueryMode2["ALL_ACTIVE"] = "all";
  FrameQueryMode2["HISTORICAL"] = "historical";
  return FrameQueryMode2;
})(FrameQueryMode || {});
class SessionManager {
  static instance;
  sessionsDir;
  currentSession = null;
  STALE_THRESHOLD = 24 * 60 * 60 * 1e3;
  // 24 hours
  constructor() {
    const homeDir = process.env["HOME"] || process.env["USERPROFILE"] || "";
    this.sessionsDir = path.join(homeDir, ".stackmemory", "sessions");
  }
  static getInstance() {
    if (!SessionManager.instance) {
      SessionManager.instance = new SessionManager();
    }
    return SessionManager.instance;
  }
  async initialize() {
    try {
      await fs.mkdir(this.sessionsDir, { recursive: true });
      await fs.mkdir(path.join(this.sessionsDir, "projects"), {
        recursive: true
      });
      await fs.mkdir(path.join(this.sessionsDir, "history"), {
        recursive: true
      });
    } catch (error) {
      throw new SystemError(
        "Failed to initialize session directories",
        ErrorCode.INITIALIZATION_ERROR,
        { error, sessionsDir: this.sessionsDir }
      );
    }
  }
  async getOrCreateSession(options) {
    if (options?.sessionId) {
      const session = await this.loadSession(options.sessionId);
      if (session) {
        this.currentSession = session;
        return session;
      }
    }
    const envSessionId = process.env["STACKMEMORY_SESSION"];
    if (envSessionId) {
      const session = await this.loadSession(envSessionId);
      if (session) {
        this.currentSession = session;
        return session;
      }
    }
    const projectHash = await this.getProjectHash(options?.projectPath);
    const branch = options?.branch || await this.getGitBranch(options?.projectPath);
    if (projectHash) {
      const branchSession = await this.findProjectBranchSession(
        projectHash,
        branch
      );
      if (branchSession && this.isSessionRecent(branchSession)) {
        await this.touchSession(branchSession);
        this.currentSession = branchSession;
        return branchSession;
      }
      const lastActive = await this.findLastActiveSession(projectHash);
      if (lastActive && this.isSessionRecent(lastActive)) {
        await this.touchSession(lastActive);
        this.currentSession = lastActive;
        return lastActive;
      }
    }
    const newSession = await this.createSession({
      projectId: projectHash || "global",
      branch,
      metadata: options?.metadata
    });
    this.currentSession = newSession;
    return newSession;
  }
  async createSession(params) {
    const session = {
      sessionId: uuidv4(),
      runId: uuidv4(),
      projectId: params.projectId,
      branch: params.branch,
      startedAt: Date.now(),
      lastActiveAt: Date.now(),
      metadata: {
        ...params.metadata,
        user: process.env["USER"],
        environment: process.env["NODE_ENV"] || "development",
        cliVersion: process.env["npm_package_version"]
      },
      state: "active"
    };
    await this.saveSession(session);
    await this.setProjectActiveSession(params.projectId, session.sessionId);
    this.currentSession = session;
    logger.info("Created new session", {
      sessionId: session.sessionId,
      projectId: session.projectId,
      branch: session.branch
    });
    return session;
  }
  async loadSession(sessionId) {
    try {
      const sessionPath = path.join(this.sessionsDir, `${sessionId}.json`);
      const data = await fs.readFile(sessionPath, "utf-8");
      return JSON.parse(data);
    } catch (error) {
      try {
        const historyPath = path.join(
          this.sessionsDir,
          "history",
          `${sessionId}.json`
        );
        const data = await fs.readFile(historyPath, "utf-8");
        return JSON.parse(data);
      } catch {
        return null;
      }
    }
  }
  async saveSession(session) {
    const sessionPath = path.join(
      this.sessionsDir,
      `${session.sessionId}.json`
    );
    await fs.writeFile(sessionPath, JSON.stringify(session, null, 2));
  }
  async suspendSession(sessionId) {
    const id = sessionId || this.currentSession?.sessionId;
    if (!id) return;
    const session = await this.loadSession(id);
    if (session) {
      session.state = "suspended";
      session.lastActiveAt = Date.now();
      await this.saveSession(session);
    }
  }
  async resumeSession(sessionId) {
    const session = await this.loadSession(sessionId);
    if (!session) {
      throw new SystemError("Session not found", ErrorCode.NOT_FOUND, {
        sessionId
      });
    }
    session.state = "active";
    session.lastActiveAt = Date.now();
    await this.saveSession(session);
    this.currentSession = session;
    return session;
  }
  async closeSession(sessionId) {
    const id = sessionId || this.currentSession?.sessionId;
    if (!id) return;
    const session = await this.loadSession(id);
    if (session) {
      session.state = "closed";
      session.lastActiveAt = Date.now();
      const sessionPath = path.join(
        this.sessionsDir,
        `${session.sessionId}.json`
      );
      const historyPath = path.join(
        this.sessionsDir,
        "history",
        `${session.sessionId}.json`
      );
      await fs.rename(sessionPath, historyPath);
    }
  }
  async listSessions(filter) {
    const sessions = [];
    const files = await fs.readdir(this.sessionsDir);
    for (const file of files) {
      if (file.endsWith(".json")) {
        const session = await this.loadSession(file.replace(".json", ""));
        if (session) {
          sessions.push(session);
        }
      }
    }
    return sessions.filter((s) => {
      if (filter?.projectId && s.projectId !== filter.projectId) return false;
      if (filter?.state && s.state !== filter.state) return false;
      if (filter?.branch && s.branch !== filter.branch) return false;
      return true;
    });
  }
  async mergeSessions(sourceId, targetId) {
    const source = await this.loadSession(sourceId);
    const target = await this.loadSession(targetId);
    if (!source || !target) {
      throw new SystemError(
        "Session not found for merge",
        ErrorCode.NOT_FOUND,
        { sourceId, targetId }
      );
    }
    target.metadata = {
      ...target.metadata,
      ...source.metadata,
      tags: [...target.metadata.tags || [], ...source.metadata.tags || []]
    };
    target.lastActiveAt = Date.now();
    await this.closeSession(sourceId);
    await this.saveSession(target);
    logger.info("Merged sessions", {
      source: sourceId,
      target: targetId
    });
    return target;
  }
  async cleanupStaleSessions(maxAge = 30 * 24 * 60 * 60 * 1e3) {
    const historyDir = path.join(this.sessionsDir, "history");
    const files = await fs.readdir(historyDir);
    const cutoff = Date.now() - maxAge;
    let cleaned = 0;
    for (const file of files) {
      if (file.endsWith(".json")) {
        const filePath = path.join(historyDir, file);
        const stats = await fs.stat(filePath);
        if (stats.mtimeMs < cutoff) {
          await fs.unlink(filePath);
          cleaned++;
        }
      }
    }
    logger.info(`Cleaned up ${cleaned} stale sessions`);
    return cleaned;
  }
  getCurrentSession() {
    return this.currentSession;
  }
  getSessionRunId() {
    return this.currentSession?.runId || uuidv4();
  }
  async getProjectHash(projectPath) {
    try {
      const cwd = projectPath || process.cwd();
      const pathModule = await import("path");
      let identifier;
      try {
        const { execSync } = await import("child_process");
        identifier = execSync("git config --get remote.origin.url", {
          cwd,
          encoding: "utf-8",
          timeout: 5e3
        }).trim();
      } catch {
        identifier = cwd;
      }
      const cleaned = identifier.replace(/\.git$/, "").replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
      return cleaned.substring(cleaned.length - 50);
    } catch {
      return null;
    }
  }
  async getGitBranch(projectPath) {
    try {
      const { execSync } = await import("child_process");
      const cwd = projectPath || process.cwd();
      const branch = execSync("git rev-parse --abbrev-ref HEAD", {
        cwd,
        encoding: "utf-8"
      }).trim();
      return branch;
    } catch {
      return void 0;
    }
  }
  async findProjectBranchSession(projectHash, branch) {
    if (!branch) return null;
    const sessions = await this.listSessions({
      projectId: projectHash,
      state: "active",
      branch
    });
    return sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt)[0] || null;
  }
  async findLastActiveSession(projectHash) {
    const sessions = await this.listSessions({
      projectId: projectHash,
      state: "active"
    });
    return sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt)[0] || null;
  }
  async setProjectActiveSession(projectId, sessionId) {
    const projectFile = path.join(
      this.sessionsDir,
      "projects",
      `${projectId}.json`
    );
    await fs.writeFile(
      projectFile,
      JSON.stringify(
        {
          projectId,
          activeSessionId: sessionId,
          updatedAt: Date.now()
        },
        null,
        2
      )
    );
  }
  isSessionRecent(session) {
    return Date.now() - session.lastActiveAt < this.STALE_THRESHOLD;
  }
  async touchSession(session) {
    session.lastActiveAt = Date.now();
    await this.saveSession(session);
  }
}
const sessionManager = SessionManager.getInstance();
export {
  FrameQueryMode,
  SessionManager,
  sessionManager
};
//# sourceMappingURL=session-manager.js.map
