/** Files currently locked by in-progress agents (agentId → Set<filePath>). */
const lockedFiles = new Map();
/**
 * C12 + C13 + C14: Analyse agent plans for file ownership overlaps.
 * Returns a AgentConflictReport with per-file conflict details and suggested resolutions.
 */
export function detectConflicts(agentPlans) {
    // Build an inverted index: file → agents that claim it
    const fileToAgents = buildFileToAgentsIndex(agentPlans);
    const conflicts = [];
    for (const [file, agents] of fileToAgents.entries()) {
        if (agents.length <= 1) {
            continue;
        }
        const resolution = suggestResolution(file, agents);
        const suggestion = buildSuggestion(file, agents, resolution);
        conflicts.push({ file, agents: [...agents], resolution, suggestion });
    }
    return {
        hasConflicts: conflicts.length > 0,
        conflicts,
    };
}
/**
 * C15: Mark files as locked by an in-progress agent.
 */
export function lockFiles(agentId, files) {
    if (!lockedFiles.has(agentId)) {
        lockedFiles.set(agentId, new Set());
    }
    const agentSet = lockedFiles.get(agentId) ?? new Set();
    lockedFiles.set(agentId, agentSet);
    for (const f of files) {
        agentSet.add(f);
    }
}
/**
 * C15: Release all file locks held by the given agent (call when agent finishes).
 */
export function releaseFileLocks(agentId) {
    lockedFiles.delete(agentId);
}
/**
 * C15: Return current locked-file state: which agent owns each locked file.
 */
export function getLockedFiles() {
    const result = {};
    for (const [agentId, files] of lockedFiles.entries()) {
        for (const file of files) {
            result[file] = agentId;
        }
    }
    return result;
}
/**
 * C16 + C17: Decompose spec criteria among available agents minimising conflicts.
 * Assigns criteria subsets and file ownership to each worker.
 */
export function decomposeSpec(specCriteria, availableAgents) {
    if (availableAgents.length === 0 || specCriteria.length === 0) {
        return [];
    }
    const agentCount = availableAgents.length;
    const chunkSize = Math.ceil(specCriteria.length / agentCount);
    const assignments = availableAgents.map((agentId, idx) => {
        const start = idx * chunkSize;
        const end = Math.min(start + chunkSize, specCriteria.length);
        const criteria = specCriteria.slice(start, end);
        return {
            agentId,
            criteria,
            fileOwnership: deriveFileOwnership(criteria, agentId),
            // Each assignment depends on all previous ones (sequential safety)
            dependencies: availableAgents.slice(0, idx),
        };
    });
    return assignments.filter((a) => a.criteria.length > 0);
}
/**
 * C18: Validate that all criteria are covered and no ownership conflicts remain.
 */
export function validateTriadCompletion(workerResults, expectedCriteria) {
    const coveredSet = new Set();
    const fileOwners = new Map();
    const ownershipConflicts = [];
    for (const result of workerResults) {
        for (const criterion of result.coveredCriteria) {
            coveredSet.add(criterion);
        }
        for (const file of result.modifiedFiles) {
            const existing = fileOwners.get(file);
            if (existing !== undefined && existing !== result.agentId) {
                ownershipConflicts.push(file);
            }
            else {
                fileOwners.set(file, result.agentId);
            }
        }
    }
    const uncoveredCriteria = expectedCriteria.filter((c) => !coveredSet.has(c));
    const isValid = uncoveredCriteria.length === 0 && ownershipConflicts.length === 0;
    const summary = buildValidationSummary(isValid, uncoveredCriteria, ownershipConflicts);
    return { isValid, uncoveredCriteria, ownershipConflicts, summary };
}
// ---------------------------------------------------------------------------
// Internal helpers
// ---------------------------------------------------------------------------
function buildFileToAgentsIndex(plans) {
    const index = new Map();
    for (const plan of plans) {
        for (const file of plan.ownedFiles) {
            const existing = index.get(file);
            if (existing) {
                existing.push(plan.agentId);
            }
            else {
                index.set(file, [plan.agentId]);
            }
        }
    }
    return index;
}
function suggestResolution(file, _agents) {
    // Heuristics based on file path
    if (file.includes('index') || file.endsWith('.json')) {
        return 'merge';
    }
    if (file.includes('test') || file.includes('spec')) {
        return 'partition';
    }
    return 'sequential';
}
function buildSuggestion(file, agents, resolution) {
    const agentList = agents.join(', ');
    if (resolution === 'sequential') {
        return `Run agents sequentially: ${agentList} should take turns modifying '${file}'.`;
    }
    if (resolution === 'partition') {
        return `Partition '${file}' by section: assign different regions to ${agentList}.`;
    }
    return `Merge strategy: each of ${agentList} should write to separate branches and merge '${file}' at the end.`;
}
function deriveFileOwnership(criteria, agentId) {
    // Produce stable pseudo-paths from criteria so ownership is deterministic
    return criteria.map((c) => {
        const slug = c
            .toLowerCase()
            .replace(/[^a-z0-9]+/g, '-')
            .replace(/^-+|-+$/g, '')
            .slice(0, 30);
        return `src/agent-work/${agentId}/${slug}.ts`;
    });
}
function buildValidationSummary(isValid, uncoveredCriteria, ownershipConflicts) {
    if (isValid) {
        return 'All criteria covered with no ownership conflicts. Triad complete.';
    }
    const parts = ['Triad validation failed.'];
    if (uncoveredCriteria.length > 0) {
        parts.push(`Uncovered criteria: ${uncoveredCriteria.join(', ')}.`);
    }
    if (ownershipConflicts.length > 0) {
        parts.push(`Ownership conflicts on: ${ownershipConflicts.join(', ')}.`);
    }
    return parts.join(' ');
}
//# sourceMappingURL=conflict-detector.js.map