/**
 * Path traversal guard — validates that resolved paths stay within allowed boundaries.
 *
 * Used by file I/O handlers to prevent path traversal attacks (CWE-22).
 */

import path from "path";
import fs from "fs";
import { settingsDb } from "../settings-db.ts";
import { homedir } from "os";
import { join } from "path";

const PLUGINS_DIR = join(homedir(), ".config", "agor", "plugins");

/** Get all configured project CWDs from settings DB. */
function getAllowedRoots(): string[] {
  const roots: string[] = [PLUGINS_DIR];
  try {
    const projects = settingsDb.listProjects();
    for (const p of projects) {
      if (typeof p.cwd === "string" && p.cwd) {
        roots.push(path.resolve(p.cwd));
      }
    }
  } catch {
    // If settings DB is unavailable, still allow plugins dir
  }
  return roots;
}

/**
 * Validate that a file path is within an allowed boundary.
 * Fix #3 (Codex audit): Uses realpathSync to resolve symlinks, preventing
 * symlink-based traversal attacks (CWE-59).
 * Returns the resolved real path if valid, or null if outside boundaries.
 */
export function validatePath(filePath: string): string | null {
  let resolved: string;
  try {
    // Resolve symlinks to their actual target to prevent symlink traversal
    resolved = fs.realpathSync(path.resolve(filePath));
  } catch {
    // If the file doesn't exist yet, resolve without symlink resolution
    // but only allow if the parent directory resolves within boundaries
    const parent = path.dirname(path.resolve(filePath));
    try {
      const realParent = fs.realpathSync(parent);
      resolved = path.join(realParent, path.basename(filePath));
    } catch {
      return null; // Parent doesn't exist either — reject
    }
  }

  const roots = getAllowedRoots();

  for (const root of roots) {
    let resolvedRoot: string;
    try {
      resolvedRoot = fs.realpathSync(path.resolve(root));
    } catch {
      resolvedRoot = path.resolve(root);
    }
    if (resolved === resolvedRoot || resolved.startsWith(resolvedRoot + path.sep)) {
      return resolved;
    }
  }

  return null;
}

/**
 * Check path and return error response if invalid.
 * Returns { valid: true, resolved: string } or { valid: false, error: string }.
 */
export function guardPath(filePath: string): { valid: true; resolved: string } | { valid: false; error: string } {
  const resolved = validatePath(filePath);
  if (resolved === null) {
    return { valid: false, error: `Access denied: path outside allowed project directories` };
  }
  return { valid: true, resolved };
}
