import { ErrorFactory } from '../exceptions/ZintrustError.js';
import * as fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
import { fileURLToPath } from '../node-singletons/url.js';
export { AwsSigV4 } from './AwsSigV4.js';
// Mutable FileChecker used to abstract FS checks for easier testing/mocking
export const FileChecker = {
    exists(filePath) {
        try {
            return fs.existsSync(filePath);
        }
        catch {
            return false;
        }
    },
};
/**
 * Common utilities - Sealed namespace for immutability
 */
export const CommonUtils = Object.freeze({
    /**
     * Resolve npm executable path from Node.js installation
     */
    resolveNpmPath() {
        const nodeBinDir = path.dirname(process.execPath);
        const candidates = process.platform === 'win32'
            ? [path.join(nodeBinDir, 'npm.cmd'), path.join(nodeBinDir, 'npm.exe')]
            : [path.join(nodeBinDir, 'npm')];
        for (const candidate of candidates) {
            if (fs.existsSync(candidate))
                return candidate;
        }
        throw ErrorFactory.createGeneralError('Unable to locate npm executable. Ensure Node.js (with npm) is installed in the standard location.');
    },
    /**
     * RUNTIME UTILITIES
     */
    /**
     * Async delay helper. No-ops when timers are unavailable.
     */
    async delay(ms) {
        if (ms <= 0)
            return;
        await new Promise((resolve) => {
            if (typeof globalThis.setTimeout !== 'function') {
                resolve();
                return;
            }
            globalThis.setTimeout(() => resolve(), ms);
        });
    },
    /**
     * Resolve an ESM `import.meta.url` to a filesystem path.
     */
    esmFilePath(metaUrl) {
        if (metaUrl === undefined || metaUrl === null)
            return '';
        if (typeof metaUrl !== 'string' && !(metaUrl instanceof URL))
            return '';
        if (typeof metaUrl === 'string' && metaUrl.trim() === '')
            return '';
        return fileURLToPath(metaUrl);
    },
    /**
     * Resolve an ESM `import.meta.url` to a filesystem directory.
     */
    esmDirname(metaUrl) {
        const resolved = this.esmFilePath(metaUrl);
        return resolved === '' ? '' : path.dirname(resolved);
    },
    /**
     * STRING UTILITIES
     */
    /**
     * Convert string to camelCase
     */
    camelCase(str) {
        return (str
            // First convert PascalCase/camelCase to snake_case format for splitting
            .replaceAll(/([a-z])([A-Z])/g, '$1_$2')
            .split(/[\s_-]+/)
            .map((word, index) => {
            if (index === 0) {
                return word.charAt(0).toLowerCase() + word.slice(1).toLowerCase();
            }
            return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
        })
            .join(''));
    },
    /**
     * Convert string to snake_case
     */
    toSnakeCase(str) {
        return str
            .replaceAll(/([a-z])([A-Z])/g, '$1_$2')
            .replaceAll(/([A-Z])/g, '_$1')
            .replaceAll(/(\d)([A-Z])/g, '$1_$2')
            .toLowerCase()
            .replace(/^_/, '')
            .replaceAll(/__+/g, '_');
    },
    /**
     * Convert string to PascalCase
     */
    toPascalCase(str) {
        return str
            .replaceAll(/([a-z])([A-Z])/g, '$1_$2') // Add underscore before capital letters in camelCase
            .split(/[\s_-]+/)
            .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
            .join('');
    },
    /**
     * FILE SYSTEM UTILITIES
     */
    /**
     * Check if file exists
     */
    fileExists(filePath) {
        return FileChecker.exists(filePath);
    },
    /**
     * Ensure directory exists, create if missing
     */
    ensureDir(dirPath) {
        if (!fs.existsSync(dirPath)) {
            fs.mkdirSync(dirPath, { recursive: true });
        }
    },
    /**
     * Best-effort variant of ensureDir() that never throws.
     */
    ensureDirSafe(dirPath) {
        try {
            this.ensureDir(dirPath);
        }
        catch {
            // best-effort
        }
    },
    /**
     * Read file contents as string
     */
    readFile(filePath) {
        try {
            return fs.readFileSync(filePath, 'utf-8');
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to read file: ${filePath}. ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    },
    /**
     * Read file contents as string (async)
     */
    async readFileAsync(filePath) {
        try {
            const fsPromises = await import('../node-singletons/fs.js').then((m) => m.fsPromises);
            return await fsPromises.readFile(filePath, 'utf-8');
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to read file: ${filePath}. ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    },
    /**
     * Write file with optional directory creation
     */
    writeFile(filePath, content, createDir = true) {
        try {
            if (createDir) {
                const dir = path.dirname(filePath);
                this.ensureDir(dir);
            }
            fs.writeFileSync(filePath, content, 'utf-8');
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to write file: ${filePath}. ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    },
    /**
     * Write file with optional directory creation (async)
     */
    async writeFileAsync(filePath, content, createDir = true) {
        try {
            const fsPromises = await import('../node-singletons/fs.js').then((m) => m.fsPromises);
            if (createDir) {
                const dir = path.dirname(filePath);
                await fsPromises.mkdir(dir, { recursive: true });
            }
            await fsPromises.writeFile(filePath, content, 'utf-8');
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to write file: ${filePath}. ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    },
    /**
     * Delete file if it exists
     */
    deleteFile(filePath) {
        try {
            if (fs.existsSync(filePath)) {
                fs.unlinkSync(filePath);
            }
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to delete file: ${filePath}. ${error instanceof Error ? error.message : 'Unknown error'}`);
        }
    },
    /**
     * TIMESTAMP UTILITIES
     */
    /**
     * Get current timestamp in ISO format
     */
    getCurrentTimestamp() {
        return new Date().toISOString();
    },
    /**
     * Format timestamp for filenames (YYYY-MM-DDTHH-MM-SS-SSSZ)
     */
    formatTimestamp(date = new Date()) {
        return date.toISOString().replaceAll(/[:.]/g, '-');
    },
    /**
     * Parse ISO timestamp string to Date
     */
    parseTimestamp(timestamp) {
        try {
            const date = new Date(timestamp);
            if (date.toString() === 'Invalid Date') {
                throw ErrorFactory.createGeneralError('Invalid date');
            }
            return date;
        }
        catch (error) {
            throw ErrorFactory.createGeneralError(`Failed to parse timestamp: ${timestamp}`, error);
        }
    },
    /**
     * VALIDATION UTILITIES
     */
    /**
     * Extract error message from unknown error type
     */
    extractErrorMessage(error) {
        if (error instanceof Error) {
            return error.message;
        }
        if (typeof error === 'string') {
            return error;
        }
        if (error !== undefined && error !== null && typeof error === 'object' && 'message' in error) {
            return String(error.message);
        }
        return 'Unknown error occurred';
    },
    /**
     * Validate options object and throw if invalid
     */
    validateOptions(options, requiredFields, context) {
        const missingFields = requiredFields.filter((field) => options[field] === undefined || options[field] === null);
        if (missingFields.length > 0) {
            throw ErrorFactory.createGeneralError(`${context}: Missing required options: ${missingFields.join(', ')}`);
        }
    },
    /**
     * Check if value is valid (not null, undefined, or empty string)
     */
    isValid(value) {
        return value !== null && value !== undefined && value !== '';
    },
    /**
     * Ensure value is string, throw otherwise
     */
    ensureString(value, fieldName) {
        if (typeof value !== 'string') {
            throw ErrorFactory.createGeneralError(`${fieldName} must be a string, got ${typeof value}`);
        }
        return value;
    },
    /**
     * Ensure value is object, throw otherwise
     */
    ensureObject(value, fieldName) {
        if (typeof value !== 'object' || value === null) {
            throw ErrorFactory.createGeneralError(`${fieldName} must be an object, got ${typeof value}`);
        }
        return value;
    },
    /**
     * Resolve preferred package manager.
     * If `preferred` is provided, the first value is returned. Otherwise we detect by lock files.
     */
    resolvePackageManager(preferred) {
        if (Array.isArray(preferred) && preferred.length > 0) {
            return preferred[0];
        }
        try {
            if (this.fileExists('pnpm-lock.yaml'))
                return 'pnpm';
            if (this.fileExists('yarn.lock'))
                return 'yarn';
            if (this.fileExists('package-lock.json'))
                return 'npm';
        }
        catch {
            // ignore FS errors and fall through to default
        }
        // Default to npm
        return 'npm';
    },
});
// Re-export for backward compatibility
export const resolveNpmPath = () => CommonUtils.resolveNpmPath();
export const delay = async (ms) => CommonUtils.delay(ms);
export const esmFilePath = (metaUrl) => CommonUtils.esmFilePath(metaUrl);
export const esmDirname = (metaUrl) => CommonUtils.esmDirname(metaUrl);
export const resolvePackageManager = (preferred) => {
    return CommonUtils.resolvePackageManager(preferred);
};
export const extractErrorMessage = (error) => CommonUtils.extractErrorMessage(error);
// Some call sites historically treated non-Error throws (e.g. string) as unknown.
// Keep that behavior available as a shared helper.
export const extractErrorMessageStrict = (error) => {
    if (error instanceof Error)
        return error.message;
    if (error !== undefined && error !== null && typeof error === 'object' && 'message' in error) {
        return String(error.message);
    }
    return 'Unknown error';
};
// Convenience named exports to make specific helpers testable and import-friendly
export const fileExists = (filePath) => CommonUtils.fileExists(filePath);
export const ensureDir = (dirPath) => CommonUtils.ensureDir(dirPath);
export const ensureDirSafe = (dirPath) => CommonUtils.ensureDirSafe(dirPath);
export const readFile = (filePath) => CommonUtils.readFile(filePath);
export const writeFile = (filePath, content, createDir = true) => CommonUtils.writeFile(filePath, content, createDir);
export const runFromSource = () => {
    const runFromSourceEnv = process.env['ZINTRUST_RUN_FROM_SOURCE'] ?? '';
    return runFromSourceEnv === '1' || runFromSourceEnv.toLowerCase() === 'true';
};
