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 {
  DatabaseError,
  ProjectError,
  ErrorCode,
  createErrorHandler
} from "../errors/index.js";
import { retry } from "../errors/recovery.js";
class ProjectManager {
  static instance;
  db;
  configPath;
  organizations = /* @__PURE__ */ new Map();
  projectCache = /* @__PURE__ */ new Map();
  currentProject;
  constructor() {
    this.configPath = join(homedir(), ".stackmemory");
    this.ensureDirectoryStructure();
    this.initializeDatabase();
    this.loadOrganizations();
    this.autoDiscoverOrganizations();
  }
  static getInstance() {
    if (!ProjectManager.instance) {
      ProjectManager.instance = new ProjectManager();
    }
    return ProjectManager.instance;
  }
  /**
   * Auto-detect project from current directory
   */
  async detectProject(projectPath) {
    const path = projectPath || process.cwd();
    const errorHandler = createErrorHandler({
      operation: "detectProject",
      projectPath: path
    });
    try {
      const cached = this.projectCache.get(path);
      if (cached && this.isCacheValid(cached)) {
        return cached;
      }
      const project = await this.analyzeProject(path);
      if (project.gitRemote) {
        project.organization = this.extractOrganization(project.gitRemote);
        project.accountType = this.determineAccountType(
          project.gitRemote,
          project.organization
        );
        project.isPrivate = this.isPrivateRepo(project.gitRemote);
      }
      project.primaryLanguage = this.detectPrimaryLanguage(path);
      project.framework = this.detectFramework(path);
      await retry(() => Promise.resolve(this.saveProject(project)), {
        maxAttempts: 3,
        initialDelay: 100,
        onRetry: (attempt, error) => {
          logger.warn(`Retrying project save (attempt ${attempt})`, {
            projectId: project.id,
            error: error instanceof Error ? error.message : String(error)
          });
        }
      });
      this.projectCache.set(path, project);
      this.currentProject = project;
      logger.info("Project auto-detected", {
        id: project.id,
        org: project.organization,
        type: project.accountType
      });
      return project;
    } catch (error) {
      const wrappedError = errorHandler(error, {
        projectPath: path,
        operation: "detectProject"
      });
      throw new ProjectError(
        `Failed to detect project at path: ${path}`,
        ErrorCode.PROJECT_INVALID_PATH,
        {
          projectPath: path,
          operation: "detectProject"
        }
      );
    }
  }
  /**
   * Analyze project directory
   */
  async analyzeProject(projectPath) {
    const gitInfo = this.getGitInfo(projectPath);
    const projectName = gitInfo.name || basename(projectPath);
    return {
      id: this.generateProjectId(gitInfo.remote || projectPath),
      name: projectName,
      path: projectPath,
      gitRemote: gitInfo.remote,
      organization: void 0,
      accountType: "personal",
      isPrivate: false,
      lastAccessed: /* @__PURE__ */ new Date(),
      metadata: {
        branch: gitInfo.branch,
        lastCommit: gitInfo.lastCommit,
        isDirty: gitInfo.isDirty
      }
    };
  }
  /**
   * Extract Git information
   */
  getGitInfo(projectPath) {
    const info = {};
    const errorHandler = createErrorHandler({
      operation: "getGitInfo",
      projectPath
    });
    try {
      info.remote = execSync("git config --get remote.origin.url", {
        cwd: projectPath,
        encoding: "utf-8",
        timeout: 5e3
        // 5 second timeout
      }).trim();
      info.branch = execSync("git branch --show-current", {
        cwd: projectPath,
        encoding: "utf-8",
        timeout: 5e3
      }).trim();
      info.lastCommit = execSync("git log -1 --format=%H", {
        cwd: projectPath,
        encoding: "utf-8",
        timeout: 5e3
      }).trim();
      const status = execSync("git status --porcelain", {
        cwd: projectPath,
        encoding: "utf-8",
        timeout: 5e3
      });
      info.isDirty = status.length > 0;
      const match = info.remote.match(/\/([^\/]+?)(\.git)?$/);
      info.name = match ? match[1] : basename(projectPath);
    } catch (error) {
      logger.debug("Git info extraction failed, using directory name", {
        projectPath,
        error: error instanceof Error ? error.message : String(error)
      });
      info.name = basename(projectPath);
    }
    return info;
  }
  /**
   * Extract organization from Git remote
   */
  extractOrganization(gitRemote) {
    const githubMatch = gitRemote.match(/github\.com[:/]([^/]+)\//);
    if (githubMatch) return githubMatch[1];
    const gitlabMatch = gitRemote.match(/gitlab\.com[:/]([^/]+)\//);
    if (gitlabMatch) return gitlabMatch[1];
    const bitbucketMatch = gitRemote.match(/bitbucket\.org[:/]([^/]+)\//);
    if (bitbucketMatch) return bitbucketMatch[1];
    const customMatch = gitRemote.match(/@([^:]+)[:/]([^/]+)\//);
    if (customMatch) return customMatch[2];
    return "unknown";
  }
  /**
   * Determine account type based on patterns
   */
  determineAccountType(gitRemote, organization) {
    for (const [, org] of this.organizations) {
      if (org.githubOrgs.includes(organization || "")) {
        return org.accountType;
      }
      for (const domain of org.domains) {
        if (gitRemote.includes(domain)) {
          return org.accountType;
        }
      }
    }
    if (organization) {
      if (organization.includes("corp") || organization.includes("company") || organization.includes("team") || organization.includes("work")) {
        return "work";
      }
      if (organization.includes("apache") || organization.includes("mozilla") || organization.includes("foundation") || gitRemote.includes("gitlab.freedesktop")) {
        return "opensource";
      }
      const username = this.getCurrentGitUser();
      if (username && organization.toLowerCase() === username.toLowerCase()) {
        return "personal";
      }
    }
    if (this.isPrivateRepo(gitRemote)) {
      const currentPath = process.cwd();
      if (currentPath.includes("/work/") || currentPath.includes("/Work/") || currentPath.includes("/company/") || currentPath.includes("/job/")) {
        return "work";
      }
    }
    return "personal";
  }
  /**
   * Check if repository is private
   */
  isPrivateRepo(gitRemote) {
    if (gitRemote.startsWith("git@")) {
      return true;
    }
    if (gitRemote.includes("@")) {
      return true;
    }
    return false;
  }
  /**
   * Detect primary programming language
   */
  detectPrimaryLanguage(projectPath) {
    const checks = [
      { file: "package.json", language: "JavaScript/TypeScript" },
      { file: "Cargo.toml", language: "Rust" },
      { file: "go.mod", language: "Go" },
      { file: "pom.xml", language: "Java" },
      { file: "requirements.txt", language: "Python" },
      { file: "Gemfile", language: "Ruby" },
      { file: "composer.json", language: "PHP" },
      { file: "*.csproj", language: "C#" },
      { file: "Podfile", language: "Swift/Objective-C" }
    ];
    for (const check of checks) {
      if (check.file.includes("*")) {
        try {
          const files = execSync(
            `find ${projectPath} -maxdepth 2 -name "${check.file}" 2>/dev/null`,
            {
              encoding: "utf-8"
            }
          );
          if (files.trim()) return check.language;
        } catch {
        }
      } else if (existsSync(join(projectPath, check.file))) {
        return check.language;
      }
    }
    return void 0;
  }
  /**
   * Detect framework
   */
  detectFramework(projectPath) {
    const packageJsonPath = join(projectPath, "package.json");
    if (existsSync(packageJsonPath)) {
      try {
        const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
        const deps = { ...pkg.dependencies, ...pkg.devDependencies };
        if (deps["next"]) return "Next.js";
        if (deps["react"]) return "React";
        if (deps["vue"]) return "Vue";
        if (deps["@angular/core"]) return "Angular";
        if (deps["express"]) return "Express";
        if (deps["fastify"]) return "Fastify";
        if (deps["@nestjs/core"]) return "NestJS";
      } catch {
      }
    }
    if (existsSync(join(projectPath, "Cargo.toml"))) {
      const cargo = readFileSync(join(projectPath, "Cargo.toml"), "utf-8");
      if (cargo.includes("actix-web")) return "Actix";
      if (cargo.includes("rocket")) return "Rocket";
    }
    return void 0;
  }
  /**
   * Get current Git user
   */
  getCurrentGitUser() {
    try {
      const email = execSync("git config --global user.email", {
        encoding: "utf-8"
      }).trim();
      const username = email.split("@")[0];
      return username;
    } catch {
      return void 0;
    }
  }
  /**
   * Generate unique project ID
   */
  generateProjectId(identifier) {
    const cleaned = identifier.replace(/\.git$/, "").replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
    return cleaned.substring(cleaned.length - 50);
  }
  /**
   * Initialize database
   */
  initializeDatabase() {
    const dbPath = join(this.configPath, "projects.db");
    const errorHandler = createErrorHandler({
      operation: "initializeDatabase",
      dbPath
    });
    try {
      this.db = new Database(dbPath);
      this.db.exec(`
        CREATE TABLE IF NOT EXISTS projects (
          id TEXT PRIMARY KEY,
          name TEXT NOT NULL,
          path TEXT NOT NULL UNIQUE,
          git_remote TEXT,
          organization TEXT,
          account_type TEXT,
          is_private BOOLEAN,
          primary_language TEXT,
          framework TEXT,
          last_accessed DATETIME,
          metadata JSON,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS organizations (
          name TEXT PRIMARY KEY,
          type TEXT,
          account_type TEXT,
          config JSON,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS project_contexts (
          id INTEGER PRIMARY KEY AUTOINCREMENT,
          project_id TEXT NOT NULL,
          context_type TEXT,
          content TEXT,
          metadata JSON,
          created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
          FOREIGN KEY (project_id) REFERENCES projects(id)
        );

        CREATE INDEX IF NOT EXISTS idx_projects_org ON projects(organization);
        CREATE INDEX IF NOT EXISTS idx_projects_type ON projects(account_type);
        CREATE INDEX IF NOT EXISTS idx_contexts_project ON project_contexts(project_id);
      `);
    } catch (error) {
      const dbError = errorHandler(error, {
        dbPath,
        operation: "initializeDatabase"
      });
      throw new DatabaseError(
        "Failed to initialize projects database",
        ErrorCode.DB_MIGRATION_FAILED,
        {
          dbPath,
          configPath: this.configPath,
          operation: "initializeDatabase"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Save project to database
   */
  saveProject(project) {
    try {
      const stmt = this.db.prepare(`
        INSERT OR REPLACE INTO projects 
        (id, name, path, git_remote, organization, account_type, is_private, 
         primary_language, framework, last_accessed, metadata, updated_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
      `);
      stmt.run(
        project.id,
        project.name,
        project.path,
        project.gitRemote,
        project.organization,
        project.accountType,
        project.isPrivate ? 1 : 0,
        project.primaryLanguage,
        project.framework,
        project.lastAccessed.toISOString(),
        JSON.stringify(project.metadata)
      );
    } catch (error) {
      throw new DatabaseError(
        `Failed to save project: ${project.name}`,
        ErrorCode.DB_QUERY_FAILED,
        {
          projectId: project.id,
          projectName: project.name,
          projectPath: project.path,
          operation: "saveProject"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Load organizations configuration
   */
  loadOrganizations() {
    const configFile = join(this.configPath, "organizations.json");
    if (existsSync(configFile)) {
      try {
        const config = JSON.parse(readFileSync(configFile, "utf-8"));
        for (const org of config.organizations || []) {
          this.organizations.set(org.name, org);
        }
      } catch (error) {
        logger.error(
          "Failed to load organizations config",
          error instanceof Error ? error : void 0
        );
      }
    }
  }
  /**
   * Auto-discover organizations from existing projects
   */
  autoDiscoverOrganizations() {
    const errorHandler = createErrorHandler({
      operation: "autoDiscoverOrganizations"
    });
    try {
      const stmt = this.db.prepare(`
        SELECT DISTINCT organization, account_type, COUNT(*) as project_count
        FROM projects
        WHERE organization IS NOT NULL
        GROUP BY organization, account_type
      `);
      const orgs = stmt.all();
      for (const org of orgs) {
        if (!this.organizations.has(org.organization)) {
          this.organizations.set(org.organization, {
            name: org.organization,
            type: org.account_type === "work" ? "company" : "personal",
            domains: [],
            githubOrgs: [org.organization],
            accountType: org.account_type,
            autoPatterns: []
          });
        }
      }
    } catch (error) {
      const wrappedError = errorHandler(error, {
        operation: "autoDiscoverOrganizations"
      });
      logger.error(
        "Failed to auto-discover organizations",
        error instanceof Error ? error : new Error(String(error)),
        {
          operation: "autoDiscoverOrganizations"
        }
      );
    }
  }
  /**
   * Ensure directory structure exists
   */
  ensureDirectoryStructure() {
    const dirs = [
      this.configPath,
      join(this.configPath, "accounts"),
      join(this.configPath, "accounts", "personal"),
      join(this.configPath, "accounts", "work"),
      join(this.configPath, "accounts", "opensource"),
      join(this.configPath, "accounts", "client"),
      join(this.configPath, "contexts"),
      join(this.configPath, "patterns")
    ];
    for (const dir of dirs) {
      if (!existsSync(dir)) {
        mkdirSync(dir, { recursive: true });
      }
    }
  }
  /**
   * Check if cache is still valid
   */
  isCacheValid(project) {
    const cacheAge = Date.now() - project.lastAccessed.getTime();
    return cacheAge < 5 * 60 * 1e3;
  }
  /**
   * Get all projects
   */
  getAllProjects() {
    try {
      const stmt = this.db.prepare(`
        SELECT * FROM projects
        ORDER BY last_accessed DESC
      `);
      const projects = stmt.all();
      return projects.map((p) => ({
        ...p,
        isPrivate: p.is_private === 1,
        lastAccessed: new Date(p.last_accessed),
        metadata: JSON.parse(p.metadata || "{}")
      }));
    } catch (error) {
      throw new DatabaseError(
        "Failed to get all projects",
        ErrorCode.DB_QUERY_FAILED,
        {
          operation: "getAllProjects"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Get projects by organization
   */
  getProjectsByOrganization(organization) {
    try {
      const stmt = this.db.prepare(`
        SELECT * FROM projects
        WHERE organization = ?
        ORDER BY last_accessed DESC
      `);
      const projects = stmt.all(organization);
      return projects.map((p) => ({
        ...p,
        isPrivate: p.is_private === 1,
        lastAccessed: new Date(p.last_accessed),
        metadata: JSON.parse(p.metadata || "{}")
      }));
    } catch (error) {
      throw new DatabaseError(
        `Failed to get projects by organization: ${organization}`,
        ErrorCode.DB_QUERY_FAILED,
        {
          organization,
          operation: "getProjectsByOrganization"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Get projects by account type
   */
  getProjectsByAccountType(accountType) {
    try {
      const stmt = this.db.prepare(`
        SELECT * FROM projects
        WHERE account_type = ?
        ORDER BY last_accessed DESC
      `);
      const projects = stmt.all(accountType);
      return projects.map((p) => ({
        ...p,
        isPrivate: p.is_private === 1,
        lastAccessed: new Date(p.last_accessed),
        metadata: JSON.parse(p.metadata || "{}")
      }));
    } catch (error) {
      throw new DatabaseError(
        `Failed to get projects by account type: ${accountType}`,
        ErrorCode.DB_QUERY_FAILED,
        {
          accountType,
          operation: "getProjectsByAccountType"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Get current project
   */
  getCurrentProject() {
    if (!this.currentProject) {
      this.detectProject();
    }
    return this.currentProject;
  }
  /**
   * Save organization config
   */
  saveOrganization(org) {
    const errorHandler = createErrorHandler({
      operation: "saveOrganization",
      orgName: org.name
    });
    try {
      this.organizations.set(org.name, org);
      const configFile = join(this.configPath, "organizations.json");
      const config = {
        organizations: Array.from(this.organizations.values())
      };
      writeFileSync(configFile, JSON.stringify(config, null, 2));
      const stmt = this.db.prepare(`
        INSERT OR REPLACE INTO organizations (name, type, account_type, config)
        VALUES (?, ?, ?, ?)
      `);
      stmt.run(org.name, org.type, org.accountType, JSON.stringify(org));
    } catch (error) {
      const wrappedError = errorHandler(error, {
        orgName: org.name,
        operation: "saveOrganization"
      });
      throw new DatabaseError(
        `Failed to save organization: ${org.name}`,
        ErrorCode.DB_QUERY_FAILED,
        {
          orgName: org.name,
          operation: "saveOrganization"
        },
        error instanceof Error ? error : void 0
      );
    }
  }
  /**
   * Auto-categorize all Git repositories in home directory
   */
  async scanAndCategorizeAllProjects(basePaths) {
    const paths = basePaths || [
      join(homedir(), "Dev"),
      join(homedir(), "dev"),
      join(homedir(), "Projects"),
      join(homedir(), "projects"),
      join(homedir(), "Work"),
      join(homedir(), "work"),
      join(homedir(), "Documents/GitHub"),
      join(homedir(), "code")
    ];
    logger.info("Scanning for Git repositories...");
    for (const basePath of paths) {
      if (!existsSync(basePath)) continue;
      try {
        const gitDirs = execSync(
          `find ${basePath} -type d -name .git -maxdepth 4 2>/dev/null`,
          { encoding: "utf-8", timeout: 3e4 }
          // 30 second timeout
        ).trim().split("\n").filter(Boolean);
        for (const gitDir of gitDirs) {
          const projectPath = dirname(gitDir);
          try {
            await this.detectProject(projectPath);
            logger.info(`Discovered project: ${projectPath}`);
          } catch (error) {
            logger.warn(`Failed to analyze project: ${projectPath}`, {
              projectPath,
              error: error instanceof Error ? error.message : String(error),
              operation: "scanAndCategorizeAllProjects"
            });
          }
        }
      } catch (error) {
        logger.warn(
          `Failed to scan ${basePath}`,
          error instanceof Error ? { error } : void 0
        );
      }
    }
    logger.info(`Scan complete. Found ${this.projectCache.size} projects`);
  }
  /**
   * Generate summary report
   */
  generateReport() {
    const allProjects = this.getAllProjects();
    const report = {
      total: allProjects.length,
      byAccountType: {},
      byOrganization: {},
      byLanguage: {},
      byFramework: {}
    };
    for (const project of allProjects) {
      report.byAccountType[project.accountType] = (report.byAccountType[project.accountType] || 0) + 1;
      if (project.organization) {
        report.byOrganization[project.organization] = (report.byOrganization[project.organization] || 0) + 1;
      }
      if (project.primaryLanguage) {
        report.byLanguage[project.primaryLanguage] = (report.byLanguage[project.primaryLanguage] || 0) + 1;
      }
      if (project.framework) {
        report.byFramework[project.framework] = (report.byFramework[project.framework] || 0) + 1;
      }
    }
    return JSON.stringify(report, null, 2);
  }
}
export {
  ProjectManager
};
//# sourceMappingURL=project-manager.js.map
