/**
 * Adapter: Merge tasks from multiple TaskSource implementations.
 *
 * CompositeTaskSource wraps N TaskSource adapters and returns the union
 * of their tasks. When two sources produce a task with the same ID,
 * the later source wins (repo overrides Content Lake). A warning is
 * logged on ID collision.
 *
 * This is how the pipeline sees both Content Lake tasks and repo-based
 * tasks in a single GeneralizedTaskDefinition[].
 *
 * @see packages/core/src/ports/task-source.ts — TaskSource port
 * @see docs/archive/exec-plans/tasks-as-content/phase-4-repo-based-tasks.md
 */
export class CompositeTaskSource {
    sources;
    constructor(sources) {
        this.sources = sources;
        if (sources.length === 0) {
            throw new Error("CompositeTaskSource requires at least one source");
        }
    }
    async loadTasks(filter) {
        const results = await Promise.all(this.sources.map((s) => s.loadTasks(filter)));
        return deduplicateById(results);
    }
}
// ---------------------------------------------------------------------------
// Deduplication — later sources override earlier ones
// ---------------------------------------------------------------------------
/**
 * Merge task arrays with later-wins deduplication.
 *
 * Sources are ordered [Content Lake, Repo, ...]. When a repo defines a
 * task with the same ID as a Content Lake task, the repo version wins
 * because it's fresher (the developer just edited it locally).
 *
 * Logs a warning on ID collision so it's visible but not blocking.
 */
function deduplicateById(taskArrays) {
    const seen = new Map();
    const merged = [];
    for (let sourceIdx = 0; sourceIdx < taskArrays.length; sourceIdx++) {
        for (const task of taskArrays[sourceIdx]) {
            const existing = seen.get(task.id);
            if (existing !== undefined) {
                // Later source wins — replace the earlier task
                console.warn(`  ⚠️  Task "${task.id}" from source ${sourceIdx} overrides source ${existing.sourceIdx}`);
                merged[existing.index] = task;
                seen.set(task.id, { index: existing.index, sourceIdx });
            }
            else {
                seen.set(task.id, { index: merged.length, sourceIdx });
                merged.push(task);
            }
        }
    }
    return merged;
}
