const fs = require('fs');
const path = require('path');

/**
 * Environment File Manager Utility
 *
 * Utilities untuk read/write .env files dengan preserve formatting
 * Support untuk update, add, remove key-value pairs
 *
 * @module lib/utils/env-manager
 */

/**
 * Read dan parse .env file
 *
 * @param {string} filePath - Path ke .env file
 * @returns {Object} Parsed environment variables
 * @returns {Object.data} - Key-value pairs
 * @returns {Object.lines} - Original lines (untuk preserve formatting)
 *
 * @example
 * const result = readEnvFile('.env');
 * // Returns: {
 * //   data: { KEY: 'value', DB_HOST: 'localhost' },
 * //   lines: ['KEY=value', '# Comment', 'DB_HOST=localhost']
 * // }
 */
function readEnvFile(filePath) {
    if (!fs.existsSync(filePath)) {
        return {
            data: {},
            lines: []
        };
    }

    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const data = {};

    for (const line of lines) {
        const trimmedLine = line.trim();

        // Skip empty lines dan comments
        if (!trimmedLine || trimmedLine.startsWith('#')) {
            continue;
        }

        // Parse KEY=VALUE
        const equalIndex = trimmedLine.indexOf('=');
        if (equalIndex > 0) {
            const key = trimmedLine.substring(0, equalIndex).trim();
            const value = trimmedLine.substring(equalIndex + 1).trim();
            data[key] = value;
        }
    }

    return {
        data,
        lines
    };
}

/**
 * Write data ke .env file dengan preserve formatting
 *
 * @param {string} filePath - Path ke .env file
 * @param {Object} data - Key-value pairs to write
 * @param {string[]} [existingLines=[]] - Original lines (untuk preserve comments)
 *
 * @example
 * writeEnvFile('.env', { KEY: 'new_value', DB_HOST: 'localhost' });
 */
function writeEnvFile(filePath, data, existingLines = []) {
    const lines = [];
    const processedKeys = new Set();

    // Preserve existing lines dan update values
    for (const line of existingLines) {
        const trimmedLine = line.trim();

        // Preserve comments dan empty lines
        if (!trimmedLine || trimmedLine.startsWith('#')) {
            lines.push(line);
            continue;
        }

        // Parse key
        const equalIndex = trimmedLine.indexOf('=');
        if (equalIndex > 0) {
            const key = trimmedLine.substring(0, equalIndex).trim();

            if (data.hasOwnProperty(key)) {
                // Update value
                lines.push(`${key}=${data[key]}`);
                processedKeys.add(key);
            } else {
                // Keep original line
                lines.push(line);
            }
        } else {
            // Keep malformed lines
            lines.push(line);
        }
    }

    // Add new keys yang belum ada
    for (const [key, value] of Object.entries(data)) {
        if (!processedKeys.has(key)) {
            lines.push(`${key}=${value}`);
        }
    }

    // Write ke file
    const content = lines.join('\n');
    fs.writeFileSync(filePath, content, 'utf8');
}

/**
 * Update atau add single key di .env file
 *
 * @param {string} filePath - Path ke .env file
 * @param {string} key - Key name
 * @param {string} value - Key value
 * @param {boolean} [createIfNotExist=true] - Create file if not exist
 *
 * @example
 * updateEnvKey('.env', 'KEY', 'rk_1a2b3c4d...');
 */
function updateEnvKey(filePath, key, value, createIfNotExist = true) {
    let envData = { data: {}, lines: [] };

    // Read existing file jika ada
    if (fs.existsSync(filePath)) {
        envData = readEnvFile(filePath);
    } else if (!createIfNotExist) {
        throw new Error(`File ${filePath} not found`);
    }

    // Update data
    envData.data[key] = value;

    // Write back
    writeEnvFile(filePath, envData.data, envData.lines);
}

/**
 * Remove atau comment out key dari .env file
 *
 * @param {string} filePath - Path ke .env file
 * @param {string} key - Key name to remove
 * @param {boolean} [softDelete=true] - Comment out instead of delete
 *
 * @example
 * // Soft delete (comment out)
 * removeEnvKey('.env', 'KEY');
 * // Hasil: #KEY=rk_1a2b3c4d...
 *
 * // Hard delete
 * removeEnvKey('.env', 'KEY', false);
 * // Hasil: line dihapus
 */
function removeEnvKey(filePath, key, softDelete = true) {
    if (!fs.existsSync(filePath)) {
        throw new Error(`File ${filePath} not found`);
    }

    const envData = readEnvFile(filePath);
    const lines = [];

    for (const line of envData.lines) {
        const trimmedLine = line.trim();

        // Check if line contains the key
        if (trimmedLine.startsWith(`${key}=`)) {
            if (softDelete) {
                // Comment out (audit trail)
                const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
                lines.push(`# Revoked at ${timestamp}`);
                lines.push(`#${line}`);
            }
            // Else: skip line (hard delete)
        } else {
            // Keep other lines
            lines.push(line);
        }
    }

    // Write back
    const content = lines.join('\n');
    fs.writeFileSync(filePath, content, 'utf8');
}

/**
 * Find all .env files di project directory
 *
 * Scans:
 * - Root directory (.env)
 * - config directory (config/*.env)
 *
 * @param {string} [baseDir=process.cwd()] - Base directory to scan
 * @returns {string[]} Array of .env file paths
 *
 * @example
 * const envFiles = findEnvFiles();
 * // Returns: [
 * //   '/project/.env',
 * //   '/project/config/dev.env',
 * //   '/project/config/prod.env'
 * // ]
 */
function findEnvFiles(baseDir = process.cwd()) {
    const envFiles = [];

    // Check root .env
    const rootEnv = path.join(baseDir, '.env');
    if (fs.existsSync(rootEnv)) {
        envFiles.push(rootEnv);
    }

    // Check config directory
    const configDir = path.join(baseDir, 'config');
    if (fs.existsSync(configDir) && fs.statSync(configDir).isDirectory()) {
        const configFiles = fs.readdirSync(configDir);

        for (const file of configFiles) {
            if (file.endsWith('.env')) {
                const filePath = path.join(configDir, file);
                envFiles.push(filePath);
            }
        }
    }

    return envFiles;
}

/**
 * Validate .env file (exists dan writable)
 *
 * @param {string} filePath - Path ke .env file
 * @returns {Object} Validation result
 * @returns {boolean} result.valid - True if valid
 * @returns {string} result.error - Error message if invalid
 *
 * @example
 * const result = validateEnvFile('.env');
 * if (!result.valid) {
 *   console.error(result.error);
 * }
 */
function validateEnvFile(filePath) {
    // Check if file exists
    if (!fs.existsSync(filePath)) {
        return {
            valid: false,
            error: `File ${filePath} not found`
        };
    }

    // Check if file is readable
    try {
        fs.accessSync(filePath, fs.constants.R_OK);
    } catch (err) {
        return {
            valid: false,
            error: `File ${filePath} tidak dapat dibaca: ${err.message}`
        };
    }

    // Check if file is writable
    try {
        fs.accessSync(filePath, fs.constants.W_OK);
    } catch (err) {
        return {
            valid: false,
            error: `File ${filePath} tidak dapat ditulis: ${err.message}`
        };
    }

    return {
        valid: true,
        error: null
    };
}

/**
 * Create backup dari .env file
 *
 * @param {string} filePath - Path ke .env file
 * @returns {string} Path to backup file
 *
 * @example
 * const backupPath = backupEnvFile('.env');
 * // Returns: '.env.backup.2025-01-10T14-30-22-123Z'
 */
function backupEnvFile(filePath) {
    if (!fs.existsSync(filePath)) {
        throw new Error(`File ${filePath} not found`);
    }

    const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
    const backupPath = `${filePath}.backup.${timestamp}`;

    fs.copyFileSync(filePath, backupPath);

    // Restrict permission ke owner-only karena .env mengandung credentials
    // (LICENSE, KEY, DB_PASSWORD). No-op pada Windows yang pakai ACL.
    if (process.platform !== 'win32') {
        fs.chmodSync(backupPath, 0o600);
    }

    return backupPath;
}

/**
 * Get value dari .env file untuk specific key
 *
 * @param {string} filePath - Path ke .env file
 * @param {string} key - Key name
 * @returns {string|null} Value or null if not found
 *
 * @example
 * const apiKey = getEnvValue('.env', 'KEY');
 * // Returns: 'rk_1a2b3c4d...' or null
 */
function getEnvValue(filePath, key) {
    const envData = readEnvFile(filePath);
    return envData.data[key] || null;
}

/**
 * Check if key exists di .env file
 *
 * @param {string} filePath - Path ke .env file
 * @param {string} key - Key name
 * @returns {boolean} True if key exists
 *
 * @example
 * if (hasEnvKey('.env', 'KEY')) {
 *   console.log('API Key already configured');
 * }
 */
function hasEnvKey(filePath, key) {
    const envData = readEnvFile(filePath);
    return envData.data.hasOwnProperty(key);
}

module.exports = {
    readEnvFile,
    writeEnvFile,
    updateEnvKey,
    removeEnvKey,
    findEnvFiles,
    validateEnvFile,
    backupEnvFile,
    getEnvValue,
    hasEnvKey
};
