// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
/**
 * @file init-scaffolding.ts
 * File-system scaffolding helpers for LumenFlow init command (WU-1644)
 *
 * Extracted from init.ts -- file creation, directory creation,
 * template loading/processing, and merge-mode handling.
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { createError, ErrorCodes } from '@lumenflow/core';
// WU-1171: Import merge block utilities
import { updateMergeBlock, MARKERS } from './merge-block.js';
import { resolveCliTemplatesDir } from './template-directory-resolver.js';
const EXECUTABLE_SCRIPT_FILE_MODE = 0o755;
/**
 * Process template content by replacing placeholders
 */
export function processTemplate(content, tokens) {
    let output = content;
    for (const [key, value] of Object.entries(tokens)) {
        // eslint-disable-next-line security/detect-non-literal-regexp -- key is from internal token map, not user input
        output = output.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
    }
    return output;
}
export function getRelativePath(targetDir, filePath) {
    return path.relative(targetDir, filePath).split(path.sep).join('/');
}
/**
 * WU-1171: Get templates directory path
 */
export function getTemplatesDir() {
    return resolveCliTemplatesDir();
}
/**
 * WU-1171: Load a template file
 */
export function loadTemplate(templatePath) {
    const templatesDir = getTemplatesDir();
    const fullPath = path.join(templatesDir, templatePath);
    if (!fs.existsSync(fullPath)) {
        throw createError(ErrorCodes.FILE_NOT_FOUND, `Template not found: ${templatePath}`);
    }
    return fs.readFileSync(fullPath, 'utf-8');
}
/**
 * Convert boolean or FileMode to FileMode
 */
export function resolveBooleanToFileMode(mode) {
    if (typeof mode === 'boolean') {
        return mode ? 'force' : 'skip';
    }
    return mode;
}
/**
 * Handle merge mode file update
 */
export function handleMergeMode(filePath, content, result, relativePath) {
    const existingContent = fs.readFileSync(filePath, 'utf-8');
    const mergeResult = updateMergeBlock(existingContent, content);
    if (mergeResult.unchanged) {
        result.skipped.push(relativePath);
        return;
    }
    if (mergeResult.warning) {
        result.warnings?.push(`${relativePath}: ${mergeResult.warning}`);
    }
    fs.writeFileSync(filePath, mergeResult.content);
    result.merged?.push(relativePath);
}
/**
 * Write a new file, creating parent directories if needed
 */
export function writeNewFile(filePath, content, result, relativePath) {
    const parentDir = path.dirname(filePath);
    if (!fs.existsSync(parentDir)) {
        fs.mkdirSync(parentDir, { recursive: true });
    }
    fs.writeFileSync(filePath, content);
    result.created.push(relativePath);
}
/**
 * Create a directory if missing
 */
export async function createDirectory(dirPath, result, targetDir) {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
        result.created.push(getRelativePath(targetDir, dirPath));
    }
}
/**
 * WU-1171: Create a file with support for skip, merge, and force modes
 *
 * @param filePath - Path to the file to create
 * @param content - Content to write (or merge block content in merge mode)
 * @param mode - 'skip' (default), 'merge', or 'force'
 * @param result - ScaffoldResult to track created/skipped/merged files
 * @param targetDir - Target directory for relative path calculation
 */
export async function createFile(filePath, content, mode, result, targetDir) {
    const relativePath = getRelativePath(targetDir, filePath);
    // Handle boolean for backwards compatibility (true = force, false = skip)
    const resolvedMode = resolveBooleanToFileMode(mode);
    // Ensure merged/warnings/overwritten arrays exist
    result.merged = result.merged ?? [];
    result.warnings = result.warnings ?? [];
    result.overwritten = result.overwritten ?? [];
    const fileExists = fs.existsSync(filePath);
    if (fileExists && resolvedMode === 'skip') {
        result.skipped.push(relativePath);
        return;
    }
    // WU-2383: merge-block mode — inject/update content between LUMENFLOW:START/END markers.
    // When file exists, preserves all user content outside markers.
    // When file doesn't exist, creates new file with content wrapped in markers.
    if (resolvedMode === 'merge-block') {
        if (fileExists) {
            const existingContent = fs.readFileSync(filePath, 'utf-8');
            const mergeResult = updateMergeBlock(existingContent, content);
            if (mergeResult.updated) {
                fs.writeFileSync(filePath, mergeResult.content);
                result.merged = result.merged ?? [];
                result.merged.push(relativePath);
            }
            else {
                result.skipped.push(relativePath);
            }
            if (mergeResult.warning) {
                result.warnings = result.warnings ?? [];
                result.warnings.push(mergeResult.warning);
            }
        }
        else {
            // New file: wrap in markers
            const wrappedContent = `${MARKERS.START}\n${content}\n${MARKERS.END}\n`;
            writeNewFile(filePath, wrappedContent, result, relativePath);
        }
        return;
    }
    if (fileExists && resolvedMode === 'merge') {
        handleMergeMode(filePath, content, result, relativePath);
        return;
    }
    // WU-1965: Track overwritten files when force mode replaces an existing file
    if (fileExists && resolvedMode === 'force') {
        result.overwritten.push(relativePath);
    }
    // Force mode or file doesn't exist: write new content
    writeNewFile(filePath, content, result, relativePath);
}
/**
 * WU-1394: Create an executable script file with proper permissions
 * Similar to createFile but sets 0o755 mode for shell scripts
 */
export async function createExecutableScript(filePath, content, mode, result, targetDir) {
    const relativePath = getRelativePath(targetDir, filePath);
    const resolvedMode = resolveBooleanToFileMode(mode);
    result.merged = result.merged ?? [];
    result.warnings = result.warnings ?? [];
    result.overwritten = result.overwritten ?? [];
    const fileExists = fs.existsSync(filePath);
    if (fileExists && resolvedMode === 'skip') {
        result.skipped.push(relativePath);
        return;
    }
    // WU-1965: Track overwritten files
    if (fileExists && (resolvedMode === 'force' || resolvedMode === 'merge')) {
        result.overwritten.push(relativePath);
    }
    // Write file with executable permissions
    const parentDir = path.dirname(filePath);
    if (!fs.existsSync(parentDir)) {
        fs.mkdirSync(parentDir, { recursive: true });
    }
    fs.writeFileSync(filePath, content, { mode: EXECUTABLE_SCRIPT_FILE_MODE });
    // chmod after write so the final mode is stable even when the process umask is restrictive.
    // eslint-disable-next-line sonarjs/file-permissions -- recovery hooks must stay executable for Claude hook dispatch.
    fs.chmodSync(filePath, EXECUTABLE_SCRIPT_FILE_MODE);
    result.created.push(relativePath);
}
//# sourceMappingURL=init-scaffolding.js.map