import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { execSync } from "child_process";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { join, basename, dirname } from "path";
import { homedir } from "os";
import Database from "better-sqlite3";
import { logger } from "../monitoring/logger.js";
import { ProjectManager } from "../projects/project-manager.js";
import {
  createErrorHandler
} from "../errors/index.js";
class WorktreeManager {
  static instance;
  config;
  configPath;
  worktreeCache = /* @__PURE__ */ new Map();
  contextMap = /* @__PURE__ */ new Map();
  db;
  constructor() {
    this.configPath = join(homedir(), ".stackmemory", "worktree-config.json");
    this.config = this.loadConfig();
    try {
      if (this.config.enabled) {
        this.initialize();
      }
    } catch (error) {
      logger.error(
        "Failed to initialize WorktreeManager",
        error instanceof Error ? error : new Error(String(error)),
        {
          configPath: this.configPath
        }
      );
      this.config.enabled = false;
    }
  }
  static getInstance() {
    if (!WorktreeManager.instance) {
      WorktreeManager.instance = new WorktreeManager();
    }
    return WorktreeManager.instance;
  }
  /**
   * Initialize worktree management
   */
  initialize() {
    const dbPath = join(homedir(), ".stackmemory", "worktrees.db");
    const errorHandler = createErrorHandler({
      operation: "initialize",
      dbPath
    });
    try {
      this.db = new Database(dbPath);
      this.db.exec(`
        CREATE TABLE IF NOT EXISTS worktrees (
          id TEXT PRIMARY KEY,
          path TEXT NOT NULL UNIQUE,
          branch TEXT NOT NULL,
          commit TEXT,
          is_main BOOLEAN,
          is_bare BOOLEAN,
          is_detached BOOLEAN,
          linked_path TEXT,
          context_id TEXT UNIQUE,
          project_id TEXT,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS worktree_contexts (
          context_id TEXT PRIMARY KEY,
          worktree_id TEXT NOT NULL,
          project_id TEXT,
          branch TEXT,
          context_path TEXT,
          db_path TEXT,
          last_synced DATETIME,
          metadata JSON,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        FOREIGN KEY (worktree_id) REFERENCES worktrees(id)
      );

      CREATE TABLE IF NOT EXISTS context_sync (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        source_context TEXT,
        target_context TEXT,
        sync_type TEXT, -- 'push', 'pull', 'merge'
        data JSON,
        synced_at DATETIME DEFAULT CURRENT_TIMESTAMP
      );

      CREATE INDEX IF NOT EXISTS idx_worktrees_project ON worktrees(project_id);
      CREATE INDEX IF NOT EXISTS idx_contexts_worktree ON worktree_contexts(worktree_id);
    `);
    } catch (error) {
      errorHandler(error);
    }
  }
  /**
   * Load configuration
   */
  loadConfig() {
    if (existsSync(this.configPath)) {
      try {
        return JSON.parse(readFileSync(this.configPath, "utf-8"));
      } catch (error) {
        logger.error("Failed to load worktree config", error);
      }
    }
    return {
      enabled: false,
      autoDetect: true,
      isolateContexts: true,
      shareGlobalContext: false,
      syncInterval: 15,
      maxWorktrees: 10
    };
  }
  /**
   * Save configuration
   */
  saveConfig(config) {
    this.config = { ...this.config, ...config };
    const configDir = dirname(this.configPath);
    if (!existsSync(configDir)) {
      mkdirSync(configDir, { recursive: true });
    }
    writeFileSync(this.configPath, JSON.stringify(this.config, null, 2));
    if (config.enabled && !this.db) {
      this.initialize();
    }
    logger.info("Worktree configuration updated", { config: this.config });
  }
  /**
   * Detect git worktrees in current repository
   */
  detectWorktrees(repoPath) {
    const path = repoPath || process.cwd();
    try {
      const output = execSync("git worktree list --porcelain", {
        cwd: path,
        encoding: "utf-8"
      });
      const worktrees = [];
      const lines = output.split("\n");
      let currentWorktree = {};
      for (const line of lines) {
        if (line.startsWith("worktree ")) {
          if (currentWorktree.path) {
            worktrees.push(this.finalizeWorktreeInfo(currentWorktree));
          }
          currentWorktree = { path: line.substring(9) };
        } else if (line.startsWith("HEAD ")) {
          currentWorktree.commit = line.substring(5);
        } else if (line.startsWith("branch ")) {
          currentWorktree.branch = line.substring(7);
        } else if (line === "bare") {
          currentWorktree.isBare = true;
        } else if (line === "detached") {
          currentWorktree.isDetached = true;
        }
      }
      if (currentWorktree.path) {
        worktrees.push(this.finalizeWorktreeInfo(currentWorktree));
      }
      if (worktrees.length > 0) {
        const mainPath = this.getMainWorktreePath(path);
        worktrees.forEach((wt) => {
          wt.isMainWorktree = wt.path === mainPath;
          if (!wt.isMainWorktree) {
            wt.linkedPath = mainPath;
          }
        });
      }
      worktrees.forEach((wt) => {
        this.worktreeCache.set(wt.path, wt);
        if (this.config.enabled) {
          this.saveWorktree(wt);
        }
      });
      logger.info(`Detected ${worktrees.length} worktrees`, {
        count: worktrees.length,
        branches: worktrees.map((w) => w.branch).filter(Boolean)
      });
      return worktrees;
    } catch (error) {
      logger.debug("Not a git repository or git worktree not available");
      return [];
    }
  }
  /**
   * Get main worktree path
   */
  getMainWorktreePath(path) {
    try {
      const gitDir = execSync("git rev-parse --git-common-dir", {
        cwd: path,
        encoding: "utf-8"
      }).trim();
      if (gitDir.includes("/.git/worktrees/")) {
        const mainGitDir = gitDir.replace(/\/\.git\/worktrees\/.*$/, "");
        return mainGitDir;
      }
      return dirname(gitDir);
    } catch {
      return path;
    }
  }
  /**
   * Finalize worktree info with defaults
   */
  finalizeWorktreeInfo(partial) {
    return {
      path: partial.path || "",
      branch: partial.branch || "detached",
      commit: partial.commit || "",
      isMainWorktree: partial.isMainWorktree || false,
      isBare: partial.isBare || false,
      isDetached: partial.isDetached || false,
      linkedPath: partial.linkedPath,
      contextId: this.generateContextId(
        partial.path || "",
        partial.branch || ""
      )
    };
  }
  /**
   * Generate unique context ID for worktree
   */
  generateContextId(path, branch) {
    const repoName = basename(dirname(path));
    const sanitizedBranch = branch.replace(/[^a-zA-Z0-9-]/g, "_");
    return `${repoName}-${sanitizedBranch}-${Buffer.from(path).toString("base64").substring(0, 8)}`;
  }
  /**
   * Save worktree to database
   */
  async saveWorktree(worktree) {
    if (!this.db) return;
    const projectManager = ProjectManager.getInstance();
    const project = await projectManager.detectProject(worktree.path);
    const stmt = this.db.prepare(`
      INSERT OR REPLACE INTO worktrees 
      (id, path, branch, commit, is_main, is_bare, is_detached, linked_path, context_id, project_id, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
    `);
    stmt.run(
      worktree.contextId,
      worktree.path,
      worktree.branch,
      worktree.commit,
      worktree.isMainWorktree ? 1 : 0,
      worktree.isBare ? 1 : 0,
      worktree.isDetached ? 1 : 0,
      worktree.linkedPath,
      worktree.contextId,
      project.id
    );
  }
  /**
   * Get or create context for worktree
   */
  getWorktreeContext(worktreePath) {
    const cached = this.contextMap.get(worktreePath);
    if (cached) {
      return cached;
    }
    const worktree = this.worktreeCache.get(worktreePath) || this.detectWorktrees(worktreePath).find(
      (w) => w.path === worktreePath
    );
    if (!worktree) {
      throw new Error(`No worktree found at path: ${worktreePath}`);
    }
    const contextBasePath = this.config.isolateContexts ? join(homedir(), ".stackmemory", "worktrees", worktree.contextId) : join(worktreePath, ".stackmemory");
    if (!existsSync(contextBasePath)) {
      mkdirSync(contextBasePath, { recursive: true });
    }
    const context = {
      worktreeId: worktree.contextId,
      projectId: "",
      // Will be filled by project manager
      branch: worktree.branch,
      contextPath: contextBasePath,
      dbPath: join(contextBasePath, "context.db"),
      lastSynced: /* @__PURE__ */ new Date(),
      metadata: {
        isMainWorktree: worktree.isMainWorktree,
        linkedPath: worktree.linkedPath
      }
    };
    if (this.db && this.config.enabled) {
      this.saveContext(context);
    }
    this.contextMap.set(worktreePath, context);
    logger.info("Created worktree context", {
      worktree: worktree.branch,
      path: contextBasePath
    });
    return context;
  }
  /**
   * Save context to database
   */
  saveContext(context) {
    if (!this.db) return;
    const stmt = this.db.prepare(`
      INSERT OR REPLACE INTO worktree_contexts
      (context_id, worktree_id, project_id, branch, context_path, db_path, last_synced, metadata)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    `);
    stmt.run(
      context.worktreeId,
      context.worktreeId,
      context.projectId,
      context.branch,
      context.contextPath,
      context.dbPath,
      context.lastSynced.toISOString(),
      JSON.stringify(context.metadata)
    );
  }
  /**
   * Sync contexts between worktrees
   */
  async syncContexts(sourceWorktree, targetWorktree, syncType = "merge") {
    const source = this.getWorktreeContext(sourceWorktree);
    const target = this.getWorktreeContext(targetWorktree);
    logger.info("Syncing contexts between worktrees", {
      source: source.branch,
      target: target.branch,
      type: syncType
    });
    const sourceDb = new Database(source.dbPath);
    const targetDb = new Database(target.dbPath);
    try {
      const contexts = sourceDb.prepare(
        `
        SELECT * FROM contexts 
        WHERE created_at > datetime('now', '-7 days')
        ORDER BY created_at DESC
      `
      ).all();
      if (syncType === "push" || syncType === "merge") {
        this.mergeContexts(contexts, targetDb, syncType === "merge");
      }
      if (syncType === "pull") {
        const targetContexts = targetDb.prepare(
          `
          SELECT * FROM contexts 
          WHERE created_at > datetime('now', '-7 days')
          ORDER BY created_at DESC
        `
        ).all();
        this.mergeContexts(targetContexts, sourceDb, false);
      }
      if (this.db) {
        const stmt = this.db.prepare(`
          INSERT INTO context_sync (source_context, target_context, sync_type, data)
          VALUES (?, ?, ?, ?)
        `);
        stmt.run(
          source.worktreeId,
          target.worktreeId,
          syncType,
          JSON.stringify({ count: contexts.length })
        );
      }
      logger.info("Context sync completed", {
        synced: contexts.length,
        type: syncType
      });
    } finally {
      sourceDb.close();
      targetDb.close();
    }
  }
  /**
   * Merge contexts into target database
   */
  mergeContexts(contexts, targetDb, bidirectional) {
    const stmt = targetDb.prepare(`
      INSERT OR REPLACE INTO contexts (id, type, content, metadata, created_at)
      VALUES (?, ?, ?, ?, ?)
    `);
    for (const ctx of contexts) {
      try {
        stmt.run(ctx.id, ctx.type, ctx.content, ctx.metadata, ctx.created_at);
      } catch (error) {
        logger.warn("Failed to merge context", { id: ctx.id, error });
      }
    }
  }
  /**
   * List all active worktrees
   */
  listActiveWorktrees() {
    if (!this.db) {
      return Array.from(this.worktreeCache.values());
    }
    const stmt = this.db.prepare(`
      SELECT * FROM worktrees
      ORDER BY is_main DESC, branch ASC
    `);
    const rows = stmt.all();
    return rows.map((row) => ({
      path: row.path,
      branch: row.branch,
      commit: row.commit,
      isMainWorktree: row.is_main === 1,
      isBare: row.is_bare === 1,
      isDetached: row.is_detached === 1,
      linkedPath: row.linked_path,
      contextId: row.context_id
    }));
  }
  /**
   * Clean up stale worktree contexts
   */
  cleanupStaleContexts() {
    if (!this.db) return;
    const activeWorktrees = this.detectWorktrees();
    const activePaths = new Set(activeWorktrees.map((w) => w.path));
    const stmt = this.db.prepare("SELECT * FROM worktrees");
    const stored = stmt.all();
    const deleteStmt = this.db.prepare("DELETE FROM worktrees WHERE id = ?");
    const deleteContextStmt = this.db.prepare(
      "DELETE FROM worktree_contexts WHERE worktree_id = ?"
    );
    for (const worktree of stored) {
      if (!activePaths.has(worktree.path)) {
        deleteStmt.run(worktree.id);
        deleteContextStmt.run(worktree.id);
        logger.info("Cleaned up stale worktree context", {
          path: worktree.path,
          branch: worktree.branch
        });
      }
    }
  }
  /**
   * Get configuration
   */
  getConfig() {
    return { ...this.config };
  }
  /**
   * Check if worktree support is enabled
   */
  isEnabled() {
    return this.config.enabled;
  }
  /**
   * Enable or disable worktree support
   */
  setEnabled(enabled) {
    this.saveConfig({ enabled });
  }
}
export {
  WorktreeManager
};
//# sourceMappingURL=worktree-manager.js.map
