/**
 * Core import-tasks logic — import tasks from cross-project export packages.
 *
 * Extracted from CLI import-tasks command for dispatch layer access.
 *
 * @task T5323, T5328, T046
 * @epic T4545
 */
import { constants as fsConstants } from 'node:fs';
import { access, readFile } from 'node:fs/promises';
import { getAccessor } from '../store/data-accessor.js';
import { detectDuplicateTitles, generateRemapTable, remapTaskReferences, resolveDuplicateTitle, } from '../store/import-remap.js';
function topologicalSort(tasks) {
    const idSet = new Set(tasks.map((t) => t.id));
    const sorted = [];
    const visited = new Set();
    function visit(task) {
        if (visited.has(task.id))
            return;
        visited.add(task.id);
        if (task.parentId && idSet.has(task.parentId)) {
            const parent = tasks.find((t) => t.id === task.parentId);
            if (parent)
                visit(parent);
        }
        for (const dep of task.depends ?? []) {
            if (idSet.has(dep)) {
                const depTask = tasks.find((t) => t.id === dep);
                if (depTask)
                    visit(depTask);
            }
        }
        sorted.push(task);
    }
    for (const task of tasks) {
        visit(task);
    }
    return sorted;
}
/**
 * Import tasks from an in-memory ExportPackage with ID remapping.
 * Core logic extracted from importTasksPackage for reuse by transfer engine.
 */
export async function importFromPackage(exportPkg, options = {}) {
    if (exportPkg._meta?.format !== 'cleo-export') {
        throw new Error(`Invalid export format (expected 'cleo-export', got '${exportPkg._meta?.format}')`);
    }
    if (!exportPkg.tasks?.length) {
        throw new Error('Export package contains no tasks');
    }
    const accessor = await getAccessor(options.cwd);
    const { tasks: existingTasks } = await accessor.queryTasks({});
    const onConflict = options.onConflict ?? 'fail';
    const onMissingDep = options.onMissingDep === 'fail' ? 'fail' : 'strip';
    const force = options.force ?? false;
    const parentId = options.parent;
    const phaseOverride = options.phase;
    const addLabel = options.addLabel;
    const resetStatus = options.resetStatus;
    const addProvenance = options.provenance !== false;
    if (parentId) {
        const parentExists = existingTasks.some((t) => t.id === parentId);
        if (!parentExists) {
            throw new Error(`Parent task not found: ${parentId}`);
        }
    }
    const sourceIds = exportPkg.tasks.map((t) => t.id);
    const remapTable = generateRemapTable(sourceIds, existingTasks);
    if (!force && onConflict === 'fail') {
        const duplicates = detectDuplicateTitles(exportPkg.tasks, existingTasks);
        if (duplicates.length > 0) {
            throw new Error(`${duplicates.length} duplicate title(s) detected. Use onConflict to resolve.`);
        }
    }
    const sortedTasks = topologicalSort(exportPkg.tasks);
    const existingIds = new Set(existingTasks.map((t) => t.id));
    const existingTitles = new Set(existingTasks.map((t) => t.title.toLowerCase()));
    const transformed = [];
    const skipped = [];
    const idRemapJson = {};
    for (const [src, dst] of remapTable.forward) {
        idRemapJson[src] = dst;
    }
    for (const task of sortedTasks) {
        if (!force) {
            const titleLower = task.title.toLowerCase();
            if (existingTitles.has(titleLower)) {
                switch (onConflict) {
                    case 'skip':
                        skipped.push(task.id);
                        continue;
                    case 'rename':
                        task.title = resolveDuplicateTitle(task.title, existingTitles);
                        break;
                    case 'duplicate':
                        break;
                    case 'fail':
                        break;
                }
            }
        }
        const remapStrategy = onMissingDep === 'fail' ? 'fail' : 'strip';
        const remapped = remapTaskReferences(task, remapTable, existingIds, remapStrategy);
        if (parentId)
            remapped.parentId = parentId;
        if (phaseOverride)
            remapped.phase = phaseOverride;
        if (addLabel) {
            remapped.labels = remapped.labels ?? [];
            if (!remapped.labels.includes(addLabel)) {
                remapped.labels.push(addLabel);
            }
        }
        if (resetStatus) {
            remapped.status = resetStatus;
        }
        if (addProvenance) {
            const originalId = remapTable.reverse.get(remapped.id) ?? remapped.id;
            const sourceProject = exportPkg._meta?.source?.project ?? 'unknown';
            const importDate = new Date().toISOString().split('T')[0];
            remapped.notes = remapped.notes ?? [];
            remapped.notes.push(`[Imported from ${sourceProject} as ${originalId} on ${importDate}]`);
        }
        remapped.updatedAt = new Date().toISOString();
        transformed.push(remapped);
        existingTitles.add(remapped.title.toLowerCase());
        existingIds.add(remapped.id);
    }
    if (options.dryRun) {
        return {
            imported: transformed.length,
            skipped: skipped.length,
            idRemap: idRemapJson,
            dryRun: true,
            preview: {
                tasks: transformed.map((t) => ({ id: t.id, title: t.title, type: t.type ?? 'task' })),
            },
        };
    }
    for (const task of transformed) {
        await accessor.upsertSingleTask(task);
    }
    return {
        imported: transformed.length,
        skipped: skipped.length,
        idRemap: idRemapJson,
    };
}
/**
 * Import tasks from a cross-project export package file with ID remapping.
 * Thin wrapper around importFromPackage that handles file I/O.
 */
export async function importTasksPackage(params) {
    const { file } = params;
    try {
        await access(file, fsConstants.R_OK);
    }
    catch {
        throw new Error(`Export file not found: ${file}`);
    }
    const content = await readFile(file, 'utf-8');
    let exportPkg;
    try {
        exportPkg = JSON.parse(content);
    }
    catch {
        throw new Error(`Invalid JSON in: ${file}`);
    }
    return importFromPackage(exportPkg, {
        cwd: params.cwd,
        dryRun: params.dryRun,
        parent: params.parent,
        phase: params.phase,
        addLabel: params.addLabel,
        provenance: params.provenance,
        resetStatus: params.resetStatus,
        onConflict: params.onConflict,
        onMissingDep: params.onMissingDep === 'placeholder' ? 'strip' : params.onMissingDep,
        force: params.force,
    });
}
//# sourceMappingURL=import-tasks.js.map