/**
 * GenerateEnvArtifacts
 * - Generates env.d.ts and .env.example from the framework's Env usage.
 *
 * Goals:
 * - Developer convenience (discoverable env keys)
 * - Keep env.d.ts + .env.example in sync with src/config usage
 *
 * Usage:
 *   tsx src/scripts/GenerateEnvArtifacts.ts
 */
import { Logger } from '../config/logger.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
import fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
const ROOT_DIR = path.resolve(process.cwd());
const GENERATED_MARKER = 'Generated by src/scripts/GenerateEnvArtifacts.ts';
const walkDir = (dirAbs) => {
    const out = [];
    const entries = fs.readdirSync(dirAbs, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'coverage')
            continue;
        const abs = path.join(dirAbs, entry.name);
        if (entry.isDirectory()) {
            out.push(...walkDir(abs));
            continue;
        }
        if (entry.isFile() && entry.name.endsWith('.ts'))
            out.push(abs);
    }
    return out;
};
const readText = (fileAbs) => fs.readFileSync(fileAbs, 'utf8');
const uniq = (arr) => Array.from(new Set(arr));
const parseEnvUsages = (content) => {
    const out = [];
    // Avoid catastrophic backtracking by not using complex regexes to parse arbitrary JS.
    // Instead scan for Env.<method>(...), handle quotes/escapes and balanced parentheses in linear time.
    const extractArgs = (parenIdx) => {
        let i = parenIdx;
        if (content[i] !== '(')
            return { args: '', endIdx: i + 1 };
        i++; // move past '('
        const start = i;
        let depth = 1;
        // Skip a quoted string starting at idx (idx points at the opening quote).
        const skipQuoted = (idx) => {
            let newIdx = idx;
            const quote = content[newIdx];
            newIdx++;
            while (newIdx < content.length) {
                if (content[newIdx] === '\\') {
                    newIdx += 2;
                    continue;
                }
                if (content[newIdx] === quote) {
                    return newIdx + 1;
                }
                newIdx++;
            }
            return newIdx;
        };
        while (i < content.length && depth > 0) {
            const ch = content[i];
            if (ch === "'" || ch === '"') {
                i = skipQuoted(i);
                continue;
            }
            if (ch === '(') {
                depth++;
                i++;
                continue;
            }
            if (ch === ')') {
                depth--;
                if (depth === 0) {
                    return { args: content.slice(start, i), endIdx: i + 1 };
                }
                i++;
                continue;
            }
            i++;
        }
        // unbalanced or EOF: return what we have
        return { args: content.slice(start, i), endIdx: i };
    };
    const methods = ['get', 'getInt', 'getBool'];
    for (const method of methods) {
        const token = `Env.${method}(`;
        let idx = content.indexOf(token, 0);
        while (idx !== -1) {
            const parenIdx = idx + token.length - 1; // position of '('
            const { args, endIdx } = extractArgs(parenIdx);
            // args looks like: ['KEY'[, defaultVal]]
            const keyMatch = /^\s*['"]([A-Z0-9_]+)['"]/.exec(args);
            if (keyMatch) {
                const key = keyMatch[1];
                const rest = args.slice(keyMatch[0].length);
                const restTrimmed = rest.replace(/^\s*,\s*/, '').trim();
                const defaultValue = restTrimmed.length > 0 ? restTrimmed : undefined;
                out.push({ key, defaultValue });
            }
            idx = content.indexOf(token, endIdx);
        }
    }
    return out;
};
const renderEnvDts = (keys) => {
    const sortedKeys = keys.toSorted((a, b) => a.localeCompare(b));
    const lines = sortedKeys.map((k) => `    ${k}?: string | undefined;`).join('\n');
    return `declare namespace NodeJS {\n  interface ProcessEnv {\n    NODE_ENV: 'development' | 'production' | 'testing' | undefined;\n\n${lines}\n  }\n}\n`;
};
const renderEnvExample = (keys) => {
    const header = `# ZinTrust Framework - Environment Configuration\n# Generated by src/scripts/GenerateEnvArtifacts.ts\n# Copy this file to .env and configure for your environment\n\n`;
    const sortedKeys = keys.toSorted((a, b) => a.localeCompare(b));
    const body = sortedKeys
        .filter((k) => k !== 'NODE_ENV')
        .map((k) => `${k}=`)
        .join('\n');
    return header + `NODE_ENV=development\n` + body + `\n`;
};
const renderEnvKeys = (keys) => {
    const sortedKeys = keys.toSorted((a, b) => a.localeCompare(b));
    return sortedKeys.join('\n') + '\n';
};
const writeIfChanged = (fileAbs, next) => {
    const prev = fs.existsSync(fileAbs) ? fs.readFileSync(fileAbs, 'utf8') : '';
    if (prev === next)
        return;
    fs.writeFileSync(fileAbs, next, 'utf8');
};
const shouldOverwriteEnvExample = (envExampleAbs) => {
    if (!fs.existsSync(envExampleAbs))
        return true;
    const prev = fs.readFileSync(envExampleAbs, 'utf8');
    return prev.includes(GENERATED_MARKER);
};
const main = () => {
    try {
        const configDir = path.join(ROOT_DIR, 'src', 'config');
        const configFiles = walkDir(configDir);
        const usages = configFiles.flatMap((f) => parseEnvUsages(readText(f)));
        const keys = uniq(usages.map((u) => u.key));
        if (keys.length === 0) {
            throw ErrorFactory.createTryCatchError('No Env.get usages found under src/config');
        }
        // env.d.ts
        const envDtsPath = path.join(ROOT_DIR, 'env.d.ts');
        writeIfChanged(envDtsPath, renderEnvDts(keys));
        // .env.example (non-destructive by default)
        const envExamplePath = path.join(ROOT_DIR, '.env.example');
        const generatedExamplePath = path.join(ROOT_DIR, '.env.example.generated');
        const renderedExample = renderEnvExample(keys);
        if (shouldOverwriteEnvExample(envExamplePath)) {
            writeIfChanged(envExamplePath, renderedExample);
        }
        else {
            writeIfChanged(generatedExamplePath, renderedExample);
        }
        // .env.keys (for quick discovery / tooling)
        const envKeysPath = path.join(ROOT_DIR, '.env.keys');
        writeIfChanged(envKeysPath, renderEnvKeys(keys));
        Logger.info(`✓ Generated env artifacts (keys: ${keys.length})`);
    }
    catch (error) {
        Logger.error('GenerateEnvArtifacts failed', error);
        process.exit(1);
    }
};
main();
