/**
 * FileManager — orchestrates template directory walking and delegates
 * filename resolution and file writing to dedicated modules.
 */
import chalk from 'chalk';
import fs from 'node:fs';
import path from 'node:path';
import { AVOID_OVERWRITING_FILES, SKIP_DIRECTORIES, } from '../domain/config.js';
import { isFilePathAllowed } from '../utils/path-allowed.js';
import { writeFile } from './file-writer.js';
import { replaceFilename } from './filename-resolver.js';
import { getTemplatesDir } from './template-engine.js';
function createGenerationResult() {
    return { lockFiles: [], originFiles: [] };
}
/**
 * Generate every file under a template element into the consumer project.
 * Main programmatic entry point of the generator engine.
 *
 * @param logger - Sink for `[FILE CREATED]`, `[FILE OVERWRITE]`, `[REGION ...]` logs.
 * @param templateElement - Which template tree to expand (see {@link TemplateElement}).
 * @param relativeTargetBasePath - Output tree relative to `process.cwd()` (`backend`, `frontend`).
 * @param relativeTargetPath - Subdirectory under the base.
 * @param options - See {@link GenerateOptions}.
 * @param result - Accumulator. Pass across multiple calls to collect combined lockfile/origin lists.
 * @returns The (possibly pre-existing) `result`, for chaining.
 */
export async function generateFromTemplate(logger, templateElement, relativeTargetBasePath, relativeTargetPath, options = {}, result = createGenerationResult()) {
    const templatesDir = getTemplatesDir();
    const originPath = path.join(templatesDir, ...templateElement.split('/'));
    await generateContents(logger, originPath, relativeTargetBasePath, relativeTargetPath, options, result);
    return result;
}
/**
 * Recursively walk a template directory and generate every file into the target.
 * Applies filename substitution, exclusion filters, and overwrite rules from {@link GenerateOptions}.
 * Prefer {@link generateFromTemplate} unless you need a custom directory outside the bundled tree.
 */
export async function generateContents(logger, originPath, relativeTargetBasePath, relativeTargetPath, options = {}, result = createGenerationResult()) {
    const targetBasePath = process.cwd();
    if (!fs.existsSync(originPath))
        return;
    const filesToCreate = fs.readdirSync(originPath);
    for (const file of filesToCreate) {
        const originFilePath = path.join(originPath, file);
        if (SKIP_DIRECTORIES.includes(file))
            continue;
        const nameReplaced = replaceFilename(file, options);
        const stats = fs.statSync(originFilePath);
        if (stats.isFile()) {
            const templatesDir = getTemplatesDir();
            const templateRelative = path
                .join(originPath.replace(templatesDir + path.sep, ''), file.replace(/\.eta$/, ''))
                .split(path.sep)
                .join('/');
            if (options.force &&
                fs.existsSync(path.join(relativeTargetBasePath, relativeTargetPath, nameReplaced)) &&
                AVOID_OVERWRITING_FILES.includes(templateRelative)) {
                continue;
            }
            const targetFilePath = path.join(relativeTargetBasePath, relativeTargetPath, nameReplaced);
            if (!isFilePathAllowed(targetFilePath, options.excludedFiles ?? [], options.excludedOperations ?? [], options.boundedContextName ?? '', options.moduleName ?? '', options.moduleNames ?? '')) {
                if (options.verbose) {
                    logger.log('%s %s excluded', chalk.yellow.inverse.bold('[EXCLUDED]'), targetFilePath);
                }
                continue;
            }
            // eslint-disable-next-line no-await-in-loop
            await writeFile(logger, originFilePath, file, options, {
                result,
                targetBasePath,
                targetRelativePath: path.join(relativeTargetBasePath, relativeTargetPath),
            });
        }
        else if (stats.isDirectory()) {
            const dirTargetPath = path.join(relativeTargetBasePath, relativeTargetPath, nameReplaced);
            if (!isFilePathAllowed(dirTargetPath, options.excludedFiles ?? [], options.excludedOperations ?? [], options.boundedContextName ?? '', options.moduleName ?? '', options.moduleNames ?? '')) {
                if (options.verbose) {
                    logger.log('%s %s excluded', chalk.yellow.inverse.bold('[EXCLUDED]'), dirTargetPath);
                }
                continue;
            }
            const absoluteDir = path.join(targetBasePath, dirTargetPath);
            if (fs.existsSync(absoluteDir)) {
                if (options.verbose) {
                    logger.log('%s Directory %s exist', chalk.yellow.bold('[DIRECTORY EXIST]'), nameReplaced);
                }
            }
            else {
                fs.mkdirSync(absoluteDir, { recursive: true });
                logger.log('%s Directory %s created', chalk.greenBright.bold('[DIRECTORY CREATED]'), nameReplaced);
            }
            // eslint-disable-next-line no-await-in-loop
            await generateContents(logger, path.join(originPath, file), relativeTargetBasePath, path.join(relativeTargetPath, nameReplaced), options, result);
        }
    }
}
