// engine/self-healing/strategies/typescript-fix.ts — SPEC-474: TypeScript fix strategy
import { execSync } from 'node:child_process';
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
/**
 * Parses TS6133 (unused import) errors from tsc output.
 * Format: path/file.ts(10,5): error TS6133: 'foo' is declared but its value is never read.
 */
function parseUnusedImports(output) {
    const pattern = /(.+?\.ts)\((\d+),\d+\): error TS6133: '(.+?)' is declared but its value is never read\./g;
    const results = [];
    for (const match of output.matchAll(pattern)) {
        const [, filePath, lineStr, name] = match;
        if (filePath !== undefined && lineStr !== undefined && name !== undefined) {
            results.push({ filePath, line: parseInt(lineStr, 10), name });
        }
    }
    return results;
}
/**
 * Removes a single unused import name from a named import line.
 * Handles: import { Foo } from '...'; and import { Foo, Bar } from '...';
 * Returns null if the line pattern is too complex to auto-fix.
 */
function removeNameFromImportLine(line, name) {
    const namedImportMatch = /^(\s*import\s+(?:type\s+)?\{)([^}]+)(\}\s+from\s+.+)$/.exec(line);
    if (namedImportMatch === null) {
        return null;
    }
    const prefix = namedImportMatch[1];
    const names = namedImportMatch[2];
    const suffix = namedImportMatch[3];
    if (prefix === undefined || names === undefined || suffix === undefined) {
        return null;
    }
    const namesList = names
        .split(',')
        .map((n) => n.trim())
        .filter(Boolean)
        .filter((n) => n !== name && n !== `type ${name}`);
    if (namesList.length === 0) {
        return '';
    }
    return `${prefix} ${namesList.join(', ')} ${suffix}`;
}
/**
 * Applies fixes for a group of TS6133 errors belonging to the same file.
 * Returns the list of fix descriptions, or empty array if nothing could be fixed.
 */
async function fixFileUnusedImports(projectPath, filePath, entries) {
    const fullPath = join(projectPath, filePath);
    let content;
    try {
        content = await readFile(fullPath, 'utf-8');
    }
    catch {
        return { fixed: [], skipped: [`${filePath}: could not read file`] };
    }
    const lines = content.split('\n');
    const fixedDescriptions = [];
    const skipped = [];
    // Sort descending by line to avoid offset shifts when splicing
    const sorted = [...entries].sort((a, b) => b.line - a.line);
    for (const entry of sorted) {
        const idx = entry.line - 1;
        const original = lines[idx];
        if (original === undefined) {
            skipped.push(`${filePath}:${entry.line.toString()}: line out of bounds`);
            continue;
        }
        const result = removeNameFromImportLine(original, entry.name);
        if (result === null) {
            skipped.push(`${filePath}:${entry.line.toString()}: complex import pattern, skipping '${entry.name}'`);
            continue;
        }
        if (result === '') {
            lines.splice(idx, 1);
        }
        else {
            lines[idx] = result;
        }
        fixedDescriptions.push(`${filePath}:${entry.line.toString()}: removed unused import '${entry.name}'`);
    }
    if (fixedDescriptions.length > 0) {
        try {
            await writeFile(fullPath, lines.join('\n'), 'utf-8');
        }
        catch {
            return {
                fixed: [],
                skipped: [...skipped, `${filePath}: could not write file`],
            };
        }
    }
    return { fixed: fixedDescriptions, skipped };
}
/**
 * Attempts to fix TypeScript errors by parsing tsc output and applying safe fixes:
 * - TS6133: removes unused imports when the pattern is unambiguous
 */
function captureTypecheckOutput(projectPath) {
    try {
        execSync('pnpm typecheck', { cwd: projectPath, stdio: 'pipe', encoding: 'utf-8' });
        return null;
    }
    catch (err) {
        const errObj = err;
        return errObj.stdout ?? errObj.stderr ?? errObj.message ?? '';
    }
}
export async function applyTypescriptFix(projectPath, _details) {
    const output = captureTypecheckOutput(projectPath);
    if (output === null) {
        return { fixed: true, reason: 'No TypeScript errors found', fixedIssues: [] };
    }
    const unusedImports = parseUnusedImports(output);
    if (unusedImports.length === 0) {
        return {
            fixed: false,
            reason: 'TypeScript errors detected but none are auto-fixable (no TS6133 unused imports found)',
        };
    }
    // Group by file for batch read/write
    const byFile = new Map();
    for (const entry of unusedImports) {
        const existing = byFile.get(entry.filePath);
        if (existing !== undefined) {
            existing.push(entry);
        }
        else {
            byFile.set(entry.filePath, [entry]);
        }
    }
    const allFixed = [];
    const allSkipped = [];
    for (const [filePath, entries] of byFile) {
        const { fixed, skipped } = await fixFileUnusedImports(projectPath, filePath, entries);
        allFixed.push(...fixed);
        allSkipped.push(...skipped);
    }
    const totalDetected = unusedImports.length;
    const totalFixed = allFixed.length;
    if (totalFixed === 0) {
        return {
            fixed: false,
            reason: `Detected ${totalDetected.toString()} TS6133 error(s) but none could be auto-fixed (complex patterns). Manual review required.`,
            fixedIssues: [],
        };
    }
    const allDone = totalFixed === totalDetected && allSkipped.length === 0;
    return {
        fixed: allDone,
        reason: allDone
            ? `Fixed all ${totalFixed.toString()} unused import(s) automatically`
            : `Fixed ${totalFixed.toString()} of ${totalDetected.toString()} unused import(s); ${allSkipped.length.toString()} required manual review`,
        fixedIssues: allFixed,
    };
}
//# sourceMappingURL=typescript-fix.js.map