/**
 * Compile command - compiles workflow files to TypeScript
 */
import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';
import { parseWorkflow } from '../../api/index.js';
import { generateInPlace } from '../../api/generate-in-place.js';
import { generateCode } from '../../api/generate.js';
import { validator } from '../../validator.js';
import { logger } from '../utils/logger.js';
import { getErrorMessage } from '../../utils/error-utils.js';
import { getFriendlyError } from '../../friendly-errors.js';
import { detectProjectModuleFormat } from './doctor.js';
import { compileTargetRegistry } from '../../generator/compile-target-registry.js';
import { AnnotationParser } from '../../parser.js';
/** Show path relative to cwd for cleaner output */
function displayPath(filePath) {
    const rel = path.relative(process.cwd(), filePath);
    // Use relative if it's shorter and doesn't escape cwd
    if (rel && !rel.startsWith('..') && rel.length < filePath.length) {
        return rel;
    }
    return filePath;
}
/**
 * Resolve the module format to use for compilation.
 * If 'auto' or not specified, detect from the project's package.json.
 */
function resolveModuleFormat(format, cwd) {
    if (format === 'esm' || format === 'cjs') {
        return format;
    }
    // Auto-detect from project
    const detection = detectProjectModuleFormat(cwd);
    return detection.format;
}
export async function compileCommand(input, options = {}) {
    const { production = false, sourceMap = false, strict = false, verbose = false, workflowName, dryRun = false, format, inlineRuntime = false, clean = false, target } = options;
    // Handle custom compile target
    if (target && target !== 'typescript') {
        return compileCustomTarget(target, input, { production, verbose, workflowName, dryRun, cron: options.cron, serve: options.serve, framework: options.framework, typedEvents: options.typedEvents, retries: options.retries, timeout: options.timeout });
    }
    // Resolve module format (auto-detect if not specified)
    const cwd = process.cwd();
    const moduleFormat = resolveModuleFormat(format, cwd);
    // If input is a directory, expand to all .ts files recursively
    let pattern = input;
    try {
        if (fs.existsSync(input) && fs.statSync(input).isDirectory()) {
            pattern = path.join(input, '**/*.ts');
        }
    }
    catch {
        // Not a valid path, use as glob pattern
    }
    // Find files matching the pattern, filter to actual files only
    const allFiles = await glob(pattern, { absolute: true });
    const files = allFiles.filter((f) => {
        try {
            return fs.statSync(f).isFile();
        }
        catch {
            return false;
        }
    });
    if (files.length === 0) {
        throw new Error(`No files found matching pattern: ${input}`);
    }
    const totalTimer = logger.timer();
    logger.section('Compiling Workflows');
    if (verbose) {
        logger.info(`Found ${files.length} file(s)`);
        logger.info(`Module format: ${moduleFormat.toUpperCase()}`);
        if (sourceMap) {
            logger.info('Source maps: enabled');
        }
        files.forEach((file) => logger.info(`  ${displayPath(file)}`));
        logger.newline();
    }
    let successCount = 0;
    let errorCount = 0;
    for (let i = 0; i < files.length; i++) {
        const file = files[i];
        const fileName = path.basename(file);
        const fileTimer = logger.timer();
        try {
            // Skip files without @flowWeaver annotations before parsing
            const rawSource = fs.readFileSync(file, 'utf8');
            if (!rawSource.includes('@flowWeaver')) {
                if (verbose) {
                    logger.info(`  ${logger.dim('skip')} ${fileName} ${logger.dim('(no @flowWeaver annotations)')}`);
                }
                continue;
            }
            // Parse the workflow
            const parseResult = await parseWorkflow(file, { workflowName, projectDir: cwd });
            if (parseResult.warnings.length > 0 && verbose) {
                logger.warn(`Parse warnings in ${fileName}:`);
                parseResult.warnings.forEach((w) => logger.warn(`  ${w}`));
            }
            if (parseResult.errors.length > 0) {
                // Skip non-workflow files silently (only error is "No workflows found")
                const isNonWorkflowFile = parseResult.errors.length === 1 &&
                    typeof parseResult.errors[0] === 'string' &&
                    parseResult.errors[0].startsWith('No workflows found');
                if (isNonWorkflowFile) {
                    if (verbose) {
                        logger.info(`  ${logger.dim('skip')} ${fileName} ${logger.dim('(no workflow)')}`);
                    }
                    continue;
                }
                logger.error(`  ${fileName}`);
                parseResult.errors.forEach((err) => logger.error(`    ${err}`));
                errorCount++;
                continue;
            }
            // Validate the AST (especially for --strict mode)
            if (strict) {
                const validation = validator.validate(parseResult.ast, { strictMode: true });
                if (validation.errors.length > 0) {
                    logger.error(`  ${fileName}`);
                    validation.errors.forEach((err) => {
                        const friendly = getFriendlyError(err);
                        if (friendly) {
                            const loc = err.location ? `[line ${err.location.line}] ` : '';
                            logger.error(`    ${loc}${friendly.title}: ${friendly.explanation}`);
                            logger.warn(`    How to fix: ${friendly.fix}`);
                            if (err.docUrl) {
                                logger.warn(`    See: ${err.docUrl}`);
                            }
                        }
                        else {
                            let msg = `    ${err.message}`;
                            if (err.node) {
                                msg += ` (node: ${err.node})`;
                            }
                            logger.error(msg);
                            if (err.docUrl) {
                                logger.warn(`    See: ${err.docUrl}`);
                            }
                        }
                    });
                    errorCount++;
                    continue;
                }
                if (validation.warnings.length > 0 && verbose) {
                    validation.warnings.forEach((warn) => {
                        const friendly = getFriendlyError(warn);
                        if (friendly) {
                            const loc = warn.location ? `[line ${warn.location.line}] ` : '';
                            logger.warn(`  ${loc}${friendly.title}: ${friendly.explanation}`);
                            logger.warn(`    How to fix: ${friendly.fix}`);
                        }
                        else {
                            logger.warn(`  ${warn.message}`);
                        }
                    });
                }
            }
            // Read original source
            const sourceCode = fs.readFileSync(file, 'utf8');
            // Generate code in-place (preserves types, interfaces, etc.)
            const result = generateInPlace(sourceCode, parseResult.ast, { production, moduleFormat, inlineRuntime, sourceFile: file, skipParamReturns: clean });
            // Write back to original file (skip in dry-run mode)
            if (!dryRun) {
                fs.writeFileSync(file, result.code, 'utf8');
                // Generate source map if requested
                if (sourceMap) {
                    const mapResult = generateCode(parseResult.ast, {
                        production,
                        sourceMap: true,
                        moduleFormat,
                    });
                    if (mapResult.sourceMap) {
                        const mapPath = file + '.map';
                        fs.writeFileSync(mapPath, mapResult.sourceMap, 'utf8');
                        const sourceMappingComment = `\n//# sourceMappingURL=${path.basename(mapPath)}\n`;
                        if (!result.code.includes('//# sourceMappingURL=')) {
                            fs.appendFileSync(file, sourceMappingComment, 'utf8');
                        }
                        if (verbose) {
                            logger.info(`    source map: ${displayPath(mapPath)}`);
                        }
                    }
                }
            }
            const timing = logger.dim(fileTimer.elapsed());
            const filePrint = displayPath(file);
            if (dryRun) {
                if (result.hasChanges) {
                    logger.success(`${filePrint} ${timing} ${logger.dim('(dry run)')}`);
                }
                else {
                    logger.log(`  ${filePrint} ${logger.dim('(no changes, dry run)')}`);
                }
            }
            else if (result.hasChanges) {
                logger.success(`${filePrint} ${timing}`);
            }
            else if (verbose) {
                logger.info(`  ${filePrint} ${logger.dim('no changes')}`);
            }
            successCount++;
        }
        catch (error) {
            logger.error(`  ${fileName}: ${getErrorMessage(error)}`);
            errorCount++;
        }
    }
    // Summary
    const elapsed = totalTimer.elapsed();
    const formatNote = verbose ? '' : ` (${moduleFormat.toUpperCase()})`;
    const dryRunNote = dryRun ? ' (dry run)' : '';
    logger.newline();
    if (errorCount > 0) {
        logger.log(`  ${successCount} compiled, ${errorCount} failed${formatNote} in ${elapsed}${dryRunNote}`);
        throw new Error(`${errorCount} file(s) failed to compile`);
    }
    else {
        logger.log(`  ${successCount} file${successCount !== 1 ? 's' : ''} compiled${formatNote} in ${elapsed}${dryRunNote}`);
    }
}
/**
 * Compile a workflow file using a registered custom compile target.
 */
export async function compileCustomTarget(target, input, options) {
    const customTarget = compileTargetRegistry.get(target);
    if (!customTarget) {
        const available = compileTargetRegistry.getNames();
        const hint = available.length > 0
            ? ` Available targets: typescript, ${available.join(', ')}`
            : ' No custom targets registered.';
        throw new Error(`Unknown compile target: ${target}.${hint}`);
    }
    const filePath = path.resolve(input);
    if (!fs.existsSync(filePath)) {
        throw new Error(`File not found: ${displayPath(filePath)}`);
    }
    logger.section(`Compiling to ${target}`);
    logger.info(`Input: ${displayPath(filePath)}`);
    logger.info(`Target: ${target}`);
    logger.newline();
    const annotationParser = new AnnotationParser();
    await annotationParser.loadPackHandlers(process.cwd());
    const parseResult = annotationParser.parse(filePath);
    if (parseResult.errors.length > 0) {
        const msgs = parseResult.errors.map((e) => `  ${e}`).join('\n');
        throw new Error(`Parse errors:\n${msgs}`);
    }
    if (parseResult.workflows.length === 0) {
        throw new Error('No workflows found in file');
    }
    const workflow = options.workflowName
        ? parseResult.workflows.find((w) => w.name === options.workflowName || w.functionName === options.workflowName)
        : parseResult.workflows[0];
    if (!workflow) {
        const available = parseResult.workflows.map((w) => w.name).join(', ');
        throw new Error(`Workflow "${options.workflowName}" not found. Available: ${available}`);
    }
    const allNodeTypes = [...(workflow.nodeTypes || [])];
    // CLI overrides for workflow options
    if (options.cron) {
        workflow.options = workflow.options || {};
        workflow.options.trigger = { ...workflow.options.trigger, cron: options.cron };
    }
    if (options.retries !== undefined) {
        workflow.options = workflow.options || {};
        workflow.options.retries = options.retries;
    }
    if (options.timeout) {
        workflow.options = workflow.options || {};
        workflow.options.timeout = options.timeout;
    }
    const code = customTarget.compile(workflow, allNodeTypes, {
        production: options.production,
        typedEvents: options.typedEvents,
        serveHandler: options.serve,
        framework: options.framework,
    });
    const outputPath = filePath.replace(/\.ts$/, `.${target}.ts`);
    if (options.dryRun) {
        logger.success(`Would generate: ${displayPath(outputPath)}`);
        logger.newline();
        logger.section('Preview');
        const lines = code.split('\n');
        const preview = lines.slice(0, 50).join('\n');
        logger.log(preview);
        if (lines.length > 50) {
            logger.info(`... (${lines.length - 50} more lines)`);
        }
    }
    else {
        fs.writeFileSync(outputPath, code, 'utf8');
        logger.success(`Compiled: ${displayPath(outputPath)}`);
    }
    logger.newline();
    logger.section('Summary');
    logger.success(`Workflow "${workflow.name}" compiled to ${target} target`);
}
//# sourceMappingURL=compile.js.map