import { readdir, readFile, stat } from 'node:fs/promises';
import { join } from 'node:path';
import TOML from '@iarna/toml';
import { isOatManagedCodexRoleFile } from '../../providers/codex/codec/shared.js';
function canonicalRoleNames(canonicalEntries) {
    return new Set(canonicalEntries
        .filter((entry) => entry.type === 'agent' && entry.isFile)
        .map((entry) => entry.name.replace(/\.md$/i, '')));
}
function toRoleNameFromTomlPath(path) {
    if (!path.startsWith('agents/') || !path.endsWith('.toml')) {
        return null;
    }
    return path.replace(/^agents\//, '').replace(/\.toml$/i, '');
}
async function fileExists(path) {
    try {
        const s = await stat(path);
        return s.isFile();
    }
    catch {
        return false;
    }
}
async function detectFromConfig(scopeRoot, existingCanonicalRoles, managedRoleNames) {
    const configPath = join(scopeRoot, '.codex', 'config.toml');
    if (!(await fileExists(configPath))) {
        return [];
    }
    let parsed;
    try {
        parsed = TOML.parse(await readFile(configPath, 'utf8'));
    }
    catch {
        return [];
    }
    const agents = parsed.agents;
    if (!agents || typeof agents !== 'object' || Array.isArray(agents)) {
        return [];
    }
    const strays = [];
    for (const [roleName, value] of Object.entries(agents)) {
        if (existingCanonicalRoles.has(roleName) ||
            managedRoleNames.has(roleName)) {
            continue;
        }
        if (!value || typeof value !== 'object' || Array.isArray(value)) {
            continue;
        }
        const roleConfig = value;
        const configFile = roleConfig.config_file;
        if (typeof configFile !== 'string') {
            continue;
        }
        const roleFromFile = toRoleNameFromTomlPath(configFile);
        if (!roleFromFile) {
            continue;
        }
        const providerPath = `.codex/${configFile}`;
        const absoluteRolePath = join(scopeRoot, providerPath);
        if (!(await fileExists(absoluteRolePath))) {
            continue;
        }
        const roleContent = await readFile(absoluteRolePath, 'utf8');
        if (isOatManagedCodexRoleFile(roleContent, roleName)) {
            continue;
        }
        strays.push({
            roleName,
            providerPath,
            description: typeof roleConfig.description === 'string'
                ? roleConfig.description
                : undefined,
        });
    }
    return strays;
}
async function detectFromAgentsDirectory(scopeRoot, existingCanonicalRoles, managedRoleNames) {
    const agentsDir = join(scopeRoot, '.codex', 'agents');
    let entries;
    try {
        entries = await readdir(agentsDir, {
            withFileTypes: true,
            encoding: 'utf8',
        });
    }
    catch {
        return [];
    }
    const strays = [];
    for (const entry of entries) {
        if (!entry.isFile() || !entry.name.endsWith('.toml')) {
            continue;
        }
        const roleName = entry.name.replace(/\.toml$/i, '');
        if (existingCanonicalRoles.has(roleName) ||
            managedRoleNames.has(roleName)) {
            continue;
        }
        const providerPath = `.codex/agents/${entry.name}`;
        const roleContent = await readFile(join(scopeRoot, providerPath), 'utf8');
        if (isOatManagedCodexRoleFile(roleContent, roleName)) {
            continue;
        }
        strays.push({
            roleName,
            providerPath,
        });
    }
    return strays;
}
export async function detectCodexRoleStrays(scopeRoot, canonicalEntries, managedRoleNames = new Set()) {
    const roleNames = canonicalRoleNames(canonicalEntries);
    const configStrays = await detectFromConfig(scopeRoot, roleNames, managedRoleNames);
    const dirStrays = await detectFromAgentsDirectory(scopeRoot, roleNames, managedRoleNames);
    const merged = new Map();
    for (const stray of [...configStrays, ...dirStrays]) {
        if (!merged.has(stray.providerPath)) {
            merged.set(stray.providerPath, stray);
        }
    }
    return Array.from(merged.values()).sort((left, right) => left.providerPath.localeCompare(right.providerPath));
}
export async function regenerateCodexAfterAdoption({ scopeRoot, scanCanonical, computeExtensionPlan, applyExtensionPlan, }) {
    const canonicalEntries = await scanCanonical();
    const extensionPlan = await computeExtensionPlan(scopeRoot, canonicalEntries);
    await applyExtensionPlan(scopeRoot, extensionPlan);
}
