/**
 * Cross-Platform Hook Utilities
 *
 * Provides platform detection and process spawning for Windows, macOS, and Linux.
 * Optimized for hook execution with minimal overhead.
 *
 * @module hooks/platform
 */
import { spawn } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
// CRITICAL: Import getCleanEnv to remove NODE_OPTIONS debugger flags from child processes
import { getCleanEnv } from '../utils/clean-env.js';
/**
 * Cached platform info (computed once at module load)
 */
const platformInfo = {
    platform: process.platform,
    isWindows: process.platform === 'win32',
    isMacOS: process.platform === 'darwin',
    isLinux: process.platform === 'linux',
    isPosix: process.platform !== 'win32',
};
/**
 * Get normalized platform identifier
 */
export function getPlatform() {
    if (platformInfo.isWindows)
        return 'windows';
    if (platformInfo.isMacOS)
        return 'macos';
    if (platformInfo.isLinux)
        return 'linux';
    return 'unknown';
}
/**
 * Check if running on Windows
 */
export function isWindows() {
    return platformInfo.isWindows;
}
/**
 * Check if running on POSIX system (macOS, Linux)
 */
export function isPosix() {
    return platformInfo.isPosix;
}
/**
 * Check WSL availability on Windows
 *
 * Checks for WSL installation without requiring it.
 * Returns cached result for performance.
 */
let wslStatusCache = null;
export function checkWslStatus() {
    if (wslStatusCache !== null) {
        return wslStatusCache;
    }
    if (!platformInfo.isWindows) {
        wslStatusCache = { available: false };
        return wslStatusCache;
    }
    try {
        // Check if wsl.exe exists
        const wslPath = path.join(process.env.SYSTEMROOT || 'C:\\Windows', 'System32', 'wsl.exe');
        if (!fs.existsSync(wslPath)) {
            wslStatusCache = { available: false };
            return wslStatusCache;
        }
        // WSL exists - we could check version but keep it simple for now
        wslStatusCache = { available: true };
        return wslStatusCache;
    }
    catch {
        wslStatusCache = { available: false };
        return wslStatusCache;
    }
}
// Re-export findProjectRoot from canonical source to avoid duplication
export { findProjectRoot } from '../utils/find-project-root.js';
/**
 * Spawn a detached background process (cross-platform)
 *
 * On Windows: Uses 'start' command or direct spawn with detached option
 * On POSIX: Uses spawn with detached + unref
 *
 * @param command - Command to run
 * @param args - Arguments
 * @param options - Spawn options
 * @returns ChildProcess or null if spawn failed
 */
export function spawnDetached(command, args = [], options = {}) {
    try {
        const spawnOptions = {
            ...options,
            detached: true,
            stdio: 'ignore',
            windowsHide: true,
        };
        // Direct spawn with detached + windowsHide works on all platforms
        // On Windows: windowsHide prevents console window, detached allows orphaning
        // On POSIX: detached creates new process group, unref allows parent to exit
        // NOTE: Previously used cmd.exe /c start /b on Windows, but that has
        // argument escaping issues and loses exit codes. Direct spawn is more reliable.
        const child = spawn(command, args, spawnOptions);
        child.unref();
        return child;
    }
    catch {
        return null;
    }
}
/**
 * Spawn Node.js script in background (cross-platform)
 *
 * Optimized for spawning Node.js scripts as background daemons.
 *
 * @param scriptPath - Path to the Node.js script
 * @param args - Arguments to pass to the script
 * @param cwd - Working directory
 * @returns ChildProcess or null if spawn failed
 */
export function spawnNodeBackground(scriptPath, args = [], cwd) {
    const nodeExecutable = process.execPath;
    try {
        // CRITICAL: Use getCleanEnv() to remove NODE_OPTIONS debugger flags
        // Without this, spawning background processes from VSCode debug mode fails silently
        const spawnOptions = {
            cwd: cwd || process.cwd(),
            detached: true,
            stdio: 'ignore',
            windowsHide: true,
            env: {
                ...getCleanEnv(),
                SPECWEAVE_BACKGROUND_PROCESS: '1',
            },
        };
        // Same spawn logic works for both Windows and POSIX
        // - Windows: windowsHide prevents console window, detached allows orphaning
        // - POSIX: detached creates new process group, unref allows parent to exit
        const child = spawn(nodeExecutable, [scriptPath, ...args], spawnOptions);
        child.unref();
        return child;
    }
    catch {
        return null;
    }
}
/**
 * Read JSON file safely with error handling
 *
 * @param filePath - Path to JSON file
 * @returns Parsed JSON or null if failed
 */
export function readJsonSafe(filePath) {
    try {
        if (!fs.existsSync(filePath)) {
            return null;
        }
        const content = fs.readFileSync(filePath, 'utf-8');
        return JSON.parse(content);
    }
    catch {
        return null;
    }
}
/**
 * Write JSON file safely with atomic write
 *
 * SAFETY: Only creates directories if the parent .specweave/ already exists.
 * This prevents accidentally creating stale .specweave/ folders in arbitrary
 * directories when code runs from a non-project CWD.
 *
 * @param filePath - Path to JSON file
 * @param data - Data to write
 * @returns true if successful, false otherwise
 */
export function writeJsonSafe(filePath, data) {
    try {
        const dir = path.dirname(filePath);
        // Guard: don't create .specweave/ from scratch
        const specweaveDir = filePath.split(path.sep).reduce((acc, part, i, parts) => {
            if (part === '.specweave')
                return parts.slice(0, i + 1).join(path.sep);
            return acc;
        }, '');
        if (specweaveDir && !fs.existsSync(specweaveDir)) {
            return false; // .specweave/ doesn't exist — refuse to create stale folder
        }
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        // Atomic write: write to temp file, then rename
        const tempPath = `${filePath}.tmp.${process.pid}`;
        fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
        fs.renameSync(tempPath, filePath);
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Append to log file (creates directory if needed)
 *
 * SAFETY: Only creates directories if the parent .specweave/ already exists.
 * This prevents accidentally creating stale .specweave/ folders in arbitrary
 * directories when hooks run from a non-project CWD.
 *
 * @param logPath - Path to log file
 * @param message - Message to append
 */
export function appendLog(logPath, message) {
    try {
        const dir = path.dirname(logPath);
        // Guard: don't create .specweave/ from scratch — only create subdirs
        // if .specweave/ already exists (i.e., this is a real project root)
        const specweaveDir = logPath.split(path.sep).reduce((acc, part, i, parts) => {
            if (part === '.specweave')
                return parts.slice(0, i + 1).join(path.sep);
            return acc;
        }, '');
        if (specweaveDir && !fs.existsSync(specweaveDir)) {
            return; // .specweave/ doesn't exist — skip to avoid creating stale folder
        }
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, { recursive: true });
        }
        const timestamp = new Date().toISOString().substring(11, 19); // HH:MM:SS
        fs.appendFileSync(logPath, `[${timestamp}] ${message}\n`, 'utf-8');
    }
    catch {
        // Silently ignore log failures
    }
}
// Re-export FileLock from canonical location for backward compatibility
export { FileLock } from '../utils/file-lock.js';
/**
 * Output hook result to stdout
 */
export function outputHookResult(result) {
    console.log(JSON.stringify(result));
}
/**
 * Consume stdin (required for hooks that receive input)
 */
export async function consumeStdin() {
    return new Promise((resolve) => {
        let data = '';
        process.stdin.setEncoding('utf-8');
        process.stdin.on('data', (chunk) => {
            data += chunk;
        });
        process.stdin.on('end', () => {
            resolve(data);
        });
        // Handle case where stdin is empty/closed
        if (process.stdin.isTTY) {
            resolve('');
        }
    });
}
/**
 * Parse hook stdin as JSON, returning typed HookInput.
 * Falls back to empty object if stdin is empty or invalid JSON.
 */
export async function parseHookInput() {
    const raw = await consumeStdin();
    if (!raw || !raw.trim())
        return {};
    try {
        return JSON.parse(raw);
    }
    catch {
        return {};
    }
}
//# sourceMappingURL=platform.js.map