import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { detectDomains } from './runtime-assets.js';
import { getImportedSpecialistProfiles } from './generated-specialist-loader.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const SPECIALIST_DETAIL_DIR = path.join(__dirname, 'generated-specialist-details');
const specialistDetailCache = new Map();
// Lazy-initialized: populated on first call to listSpecialists() / getSpecialist() / etc.
// Data loaded from src/engines/data/specialist-profiles.json (defers 8 k LOC parse to first use).
let _specialists = null;
let _crews = null;
function ensureSpecialists() {
    if (!_specialists) {
        _specialists = getImportedSpecialistProfiles().map(withLazyDetails).sort((left, right) => left.name.localeCompare(right.name));
    }
    return _specialists;
}
function ensureCrews() {
    if (!_crews) {
        _crews = buildCrewTemplates(ensureSpecialists());
    }
    return _crews;
}
export function listSpecialists() {
    return ensureSpecialists();
}
export function getSpecialist(specialistId) {
    const needle = specialistId.toLowerCase();
    return ensureSpecialists().find((specialist) => specialist.specialistId.toLowerCase() === needle ||
        specialist.name.toLowerCase() === needle);
}
export function listCrewTemplates() {
    return ensureCrews();
}
export function getCrewTemplate(crewId) {
    const needle = crewId.toLowerCase();
    return ensureCrews().find((crew) => crew.crewId.toLowerCase() === needle || crew.name.toLowerCase() === needle);
}
export function planSpecialists(input) {
    const requestedCrews = input.requestedCrews ?? [];
    const requestedSpecialists = input.requestedSpecialists ?? [];
    const requestedSkills = input.requestedSkills ?? [];
    const requestedWorkflows = input.requestedWorkflows ?? [];
    const goal = input.goal;
    const matchedDomains = detectDomains(goal, [
        ...(input.files ?? []),
        ...requestedCrews,
        ...requestedSpecialists,
        ...requestedSkills,
        ...requestedWorkflows,
    ]);
    const taskSignals = analyzeTaskContext(goal, matchedDomains, input.files ?? []);
    const selectedCrew = selectCrew(goal, matchedDomains, requestedCrews);
    const selectedSpecialists = rankSpecialists(goal, matchedDomains, selectedCrew, requestedSpecialists, input.optimizationProfile ?? 'standard');
    const selectedSkills = pinRequestedSelectors(requestedSkills, rankAssetSelectors(goal, matchedDomains, input.files ?? [], dedupeStrings([
        ...requestedSkills,
        ...selectedSpecialists.flatMap((entry) => getSpecialist(entry.specialistId)?.recommendedSkills ?? []),
    ]), input.optimizationProfile ?? 'standard'));
    const selectedWorkflows = pinRequestedSelectors(requestedWorkflows, rankAssetSelectors(goal, matchedDomains, input.files ?? [], dedupeStrings([
        ...requestedWorkflows,
        ...selectedSpecialists.flatMap((entry) => getSpecialist(entry.specialistId)?.recommendedWorkflows ?? []),
    ]), input.optimizationProfile ?? 'standard'));
    const toolPolicy = selectToolPolicy(selectedSpecialists);
    const fallbackPlan = {
        summary: 'Fallback to current runtime domain-pack execution if specialist confidence or crew coverage is weak.',
        steps: [
            'Drop to the selected crew fallback if a required specialist is missing.',
            'Reduce to advisory/review specialists only if mutate authority is uncertain.',
            'Fall back to current skill/workflow/domain resolution if confidence remains low.',
        ],
    };
    const reviewGates = buildReviewGates(selectedCrew, selectedSpecialists, {
        goal,
        matchedDomains,
        files: input.files ?? [],
        signals: taskSignals,
    });
    const continuation = {
        summary: 'After completion, queue bounded next-step recommendations through continuation-capable specialists.',
        suggestedActions: [
            'Capture follow-up backlog items from PM and architecture review.',
            'Route release-facing changes to DevOps and marketer/docs review when applicable.',
        ],
        automationCandidates: ['release-followup', 'docs-sync', 'postmortem-synthesis'],
    };
    const ledger = [
        {
            stage: 'objective',
            status: 'completed',
            owner: 'intake-planner',
            selectedAssets: [goal.slice(0, 96)],
            notes: `Domains matched: ${matchedDomains.join(', ') || 'generic'}`,
        },
        {
            stage: 'crew-selection',
            status: 'completed',
            owner: 'crew-selector',
            selectedAssets: [selectedCrew.name],
            notes: selectedCrew.confidence.reasons.join(' | ') || 'Crew inferred from domain and explicit selectors.',
        },
        {
            stage: 'specialist-selection',
            status: 'completed',
            owner: 'specialist-selector',
            selectedAssets: selectedSpecialists.map((entry) => entry.name),
            notes: `${selectedSpecialists.length} specialists selected.`,
        },
        {
            stage: 'skill-selection',
            status: 'completed',
            owner: 'skill-selector',
            selectedAssets: selectedSkills,
            notes: selectedSkills.length > 0 ? 'Selected from explicit request + specialist affinity.' : 'No specialist-specific skills selected; runtime defaults remain active.',
        },
        {
            stage: 'workflow-selection',
            status: 'completed',
            owner: 'workflow-selector',
            selectedAssets: selectedWorkflows,
            notes: selectedWorkflows.length > 0 ? 'Selected from explicit request + specialist affinity.' : 'Workflow resolution will fall back to current runtime selection.',
        },
        {
            stage: 'tool-policy',
            status: 'completed',
            owner: 'tool-policy-selector',
            selectedAssets: toolPolicy.allowedTools,
            notes: toolPolicy.reasons.join(' | '),
        },
        {
            stage: 'fallback',
            status: 'planned',
            owner: 'architect-reviewer',
            selectedAssets: fallbackPlan.steps,
            notes: fallbackPlan.summary,
        },
        {
            stage: 'review-gates',
            status: 'planned',
            owner: 'cto-reviewer',
            selectedAssets: reviewGates.map((gate) => gate.gate),
            notes: 'Review and ship stages remain additive overlays on top of the current runtime.',
        },
    ];
    return {
        selectedCrew,
        selectedSpecialists,
        selectedSkills,
        selectedWorkflows,
        toolPolicy,
        fallbackPlan,
        reviewGates,
        continuation,
        ledger,
    };
}
function withLazyDetails(seed) {
    const profile = { ...seed };
    Object.defineProperty(profile, 'rawMarkdown', {
        enumerable: true,
        get() {
            return loadSpecialistDetail(seed.specialistId).rawMarkdown;
        },
    });
    Object.defineProperty(profile, 'sections', {
        enumerable: true,
        get() {
            return loadSpecialistDetail(seed.specialistId).sections;
        },
    });
    return profile;
}
function loadSpecialistDetail(specialistId) {
    const cached = specialistDetailCache.get(specialistId);
    if (cached)
        return cached;
    const detailPath = path.join(SPECIALIST_DETAIL_DIR, `${specialistId}.json`);
    if (!fs.existsSync(detailPath)) {
        const fallback = { rawMarkdown: '', sections: {} };
        specialistDetailCache.set(specialistId, fallback);
        return fallback;
    }
    const parsed = JSON.parse(fs.readFileSync(detailPath, 'utf8'));
    specialistDetailCache.set(specialistId, parsed);
    return parsed;
}
function buildCrewTemplates(specialists) {
    const byName = (parts) => specialists
        .filter((specialist) => parts.every((part) => specialist.name.toLowerCase().includes(part)))
        .map((specialist) => specialist.specialistId);
    const requireDivision = (division, limit) => specialists
        .filter((specialist) => specialist.division === division)
        .slice(0, limit)
        .map((specialist) => specialist.specialistId);
    const gates = (...values) => values;
    return [
        {
            crewId: 'crew_pdlc',
            name: 'PDLC Crew',
            summary: 'Sequential planning, implementation, QA, and shipping for product delivery.',
            domains: ['pdlc', 'product', 'backend', 'frontend'],
            requiredSpecialists: dedupeStrings([
                ...byName(['project']),
                ...byName(['frontend']),
                ...byName(['backend']),
                ...byName(['evidence']),
            ]).slice(0, 5),
            optionalSpecialists: requireDivision('product', 3),
            reviewGates: gates('pm', 'architecture', 'code-review', 'cto', 'devops', 'marketer-docs'),
            fallbackCrewId: 'crew_implementation',
        },
        {
            crewId: 'crew_implementation',
            name: 'Implementation Crew',
            summary: 'Execution-focused engineering crew with verifier and release safety.',
            domains: ['backend', 'frontend', 'typescript', 'node', 'python', 'django', 'react'],
            requiredSpecialists: dedupeStrings([
                ...byName(['frontend']),
                ...byName(['backend']),
                ...byName(['senior', 'developer']),
                ...byName(['reality']),
            ]).slice(0, 5),
            optionalSpecialists: dedupeStrings([
                ...byName(['devops']),
                ...byName(['security']),
                ...byName(['performance']),
            ]).slice(0, 4),
            reviewGates: gates('architecture', 'code-review', 'cto', 'devops'),
            fallbackCrewId: 'crew_research',
        },
        {
            crewId: 'crew_gtm',
            name: 'GTM Crew',
            summary: 'Launch, growth, messaging, and content specialists working from shipped capability.',
            domains: ['gtm', 'marketing', 'sales', 'writing'],
            requiredSpecialists: dedupeStrings([
                ...byName(['growth']),
                ...byName(['content']),
                ...byName(['social']),
                ...byName(['executive', 'summary']),
            ]).slice(0, 5),
            optionalSpecialists: dedupeStrings([
                ...requireDivision('marketing', 5),
                ...requireDivision('paid-media', 3),
                ...requireDivision('strategy', 3),
            ]).slice(0, 5),
            reviewGates: gates('pm', 'architecture', 'cto', 'marketer-docs'),
            fallbackCrewId: 'crew_content',
        },
        {
            crewId: 'crew_content',
            name: 'Content Crew',
            summary: 'Writing, storytelling, and distribution specialists for content and docs surfaces.',
            domains: ['writing', 'marketing'],
            requiredSpecialists: dedupeStrings([
                ...byName(['content']),
                ...byName(['storyteller']),
                ...byName(['executive', 'summary']),
            ]).slice(0, 4),
            optionalSpecialists: requireDivision('marketing', 4),
            reviewGates: gates('pm', 'cto', 'marketer-docs'),
        },
        {
            crewId: 'crew_finance',
            name: 'Finance Crew',
            summary: 'Finance, analytics, and strategic review specialists for business and economics tasks.',
            domains: ['finance', 'economics', 'data'],
            requiredSpecialists: dedupeStrings([
                ...byName(['finance']),
                ...byName(['analytics']),
                ...requireDivision('strategy', 2),
            ]).slice(0, 4),
            optionalSpecialists: requireDivision('support', 3),
            reviewGates: gates('pm', 'cto'),
        },
        {
            crewId: 'crew_security',
            name: 'Security Crew',
            summary: 'Threat, trust, identity, and secure release specialists.',
            domains: ['security', 'trust', 'identity'],
            requiredSpecialists: dedupeStrings([
                ...byName(['security']),
                ...byName(['identity']),
                ...byName(['trust']),
            ]).slice(0, 4),
            optionalSpecialists: dedupeStrings([
                ...byName(['backend']),
                ...byName(['devops']),
            ]).slice(0, 3),
            reviewGates: gates('architecture', 'code-review', 'cto', 'devops'),
            fallbackCrewId: 'crew_implementation',
        },
        {
            crewId: 'crew_research',
            name: 'Research Crew',
            summary: 'Research, architecture, analysis, and evidence collection for uncertain tasks.',
            domains: ['deep-tech', 'research', 'ai'],
            requiredSpecialists: dedupeStrings([
                ...byName(['trend']),
                ...byName(['ux', 'researcher']),
                ...byName(['tool', 'evaluator']),
                ...byName(['data', 'analytics']),
            ]).slice(0, 4),
            optionalSpecialists: dedupeStrings([
                ...requireDivision('specialized', 4),
                ...requireDivision('testing', 3),
            ]).slice(0, 5),
            reviewGates: gates('pm', 'architecture', 'cto'),
        },
    ].map((crew) => ({
        ...crew,
        requiredSpecialists: dedupeStrings(crew.requiredSpecialists),
        optionalSpecialists: dedupeStrings(crew.optionalSpecialists.filter((id) => !crew.requiredSpecialists.includes(id))),
    }));
}
function selectCrew(goal, matchedDomains, requestedCrews) {
    const requestSet = new Set(requestedCrews.map((value) => value.toLowerCase()));
    const technicalAudit = analyzeTaskContext(goal, matchedDomains).technical;
    const ranked = ensureCrews()
        .map((crew) => {
        let score = 0.1;
        const reasons = [];
        if (requestSet.has(crew.crewId.toLowerCase()) || requestSet.has(crew.name.toLowerCase())) {
            score += 0.7;
            reasons.push('Explicit crew selector matched.');
        }
        const domainHits = crew.domains.filter((domain) => matchedDomains.includes(domain));
        if (domainHits.length > 0) {
            score += Math.min(0.4, domainHits.length * 0.12);
            reasons.push(`Matched domains: ${domainHits.join(', ')}`);
        }
        if (goal.toLowerCase().includes('release')) {
            if (crew.reviewGates.includes('devops')) {
                score += 0.15;
                reasons.push('Includes DevOps shipping gate.');
            }
        }
        if (technicalAudit && crew.crewId === 'crew_implementation') {
            score += 0.35;
            reasons.push('Technical control-plane audit prefers implementation crew.');
        }
        if (technicalAudit && crew.domains.some((domain) => ['backend', 'frontend', 'api', 'orchestration'].includes(domain))) {
            score += 0.12;
            reasons.push('Crew covers engineering/control-plane domains.');
        }
        if (technicalAudit && ['crew_pdlc', 'crew_gtm', 'crew_research'].includes(crew.crewId) && !crew.domains.some((domain) => ['backend', 'frontend', 'api', 'orchestration'].includes(domain))) {
            score -= 0.12;
            reasons.push('Broad planning/marketing crews are deprioritized for technical audits.');
        }
        return { crew, confidence: { score: Math.min(score, 0.99), reasons } };
    })
        .sort((left, right) => right.confidence.score - left.confidence.score);
    const chosen = ranked[0] ?? {
        crew: ensureCrews()[0],
        confidence: { score: 0.4, reasons: ['Defaulted to the first available crew.'] },
    };
    return {
        crewId: chosen.crew.crewId,
        name: chosen.crew.name,
        summary: chosen.crew.summary,
        domains: chosen.crew.domains,
        confidence: chosen.confidence,
    };
}
function rankSpecialists(goal, matchedDomains, crew, requestedSpecialists, optimizationProfile) {
    const requestSet = new Set(requestedSpecialists.map((value) => value.toLowerCase()));
    const crewTemplate = getCrewTemplate(crew.crewId);
    const seededIds = new Set([...(crewTemplate?.requiredSpecialists ?? []), ...(crewTemplate?.optionalSpecialists ?? [])]);
    const signals = analyzeTaskContext(goal, matchedDomains);
    const ranked = ensureSpecialists().map((specialist) => {
        let score = 0.05;
        const reasons = [];
        if (seededIds.has(specialist.specialistId)) {
            score += 0.35;
            reasons.push(`Included in ${crew.name}.`);
        }
        if (requestSet.has(specialist.specialistId.toLowerCase()) || requestSet.has(specialist.name.toLowerCase())) {
            score += 0.45;
            reasons.push('Explicit specialist selector matched.');
        }
        const domainHits = specialist.domains.filter((domain) => matchedDomains.includes(domain));
        if (domainHits.length > 0) {
            score += Math.min(0.3, domainHits.length * 0.08);
            reasons.push(`Matched domains: ${domainHits.join(', ')}`);
        }
        if (goal.toLowerCase().includes('review') && specialist.authority !== 'mutate') {
            score += 0.08;
            reasons.push('Review-oriented task prefers advisory/review specialists.');
        }
        if (goal.toLowerCase().includes('implement') && specialist.authority === 'mutate') {
            score += 0.08;
            reasons.push('Implementation task prefers mutate-capable specialists.');
        }
        if (signals.technical && isCrossDomainSpecialist(specialist) && !requestSet.has(specialist.specialistId.toLowerCase()) && !requestSet.has(specialist.name.toLowerCase())) {
            score -= 0.42;
            reasons.push('Cross-domain marketing or product specialist deprioritized for a technical task.');
        }
        if (signals.technical && specialist.domains.some((domain) => ['backend', 'frontend', 'typescript', 'node', 'python', 'react', 'api', 'security', 'trust', 'ai', 'research'].includes(domain))) {
            score += 0.14;
            reasons.push('Engineering, trust, or AI domain matches a technical task.');
        }
        if (signals.docsOrPitch && specialist.division === 'marketing') {
            score += 0.12;
            reasons.push('Docs or pitch-facing work allows marketing and writing specialists.');
        }
        return {
            specialist,
            confidence: {
                score: Math.min(score, 0.99),
                reasons,
            },
        };
    }).sort((left, right) => right.confidence.score - left.confidence.score);
    const take = optimizationProfile === 'max' ? 6 : 4;
    return ranked
        .filter((entry) => entry.confidence.score >= 0.2 || seededIds.has(entry.specialist.specialistId))
        .slice(0, take)
        .map((entry) => ({
        specialistId: entry.specialist.specialistId,
        name: entry.specialist.name,
        division: entry.specialist.division,
        authority: entry.specialist.authority,
        confidence: entry.confidence,
    }));
}
function selectToolPolicy(selectedSpecialists) {
    const specialists = selectedSpecialists
        .map((entry) => getSpecialist(entry.specialistId))
        .filter(Boolean);
    const allowedTools = dedupeStrings(specialists.flatMap((specialist) => specialist.tools));
    return {
        allowedTools: allowedTools.length > 0 ? allowedTools : ['read_file', 'run_command'],
        reasons: [
            'Selected from specialist tool preferences and authority constraints.',
            specialists.some((specialist) => specialist.authority === 'mutate')
                ? 'Mutate-capable specialists enabled write-capable tools.'
                : 'Advisory/review specialists kept the tool policy conservative.',
        ],
    };
}
function buildReviewGates(crew, selectedSpecialists, context) {
    const owner = selectedSpecialists[0]?.name ?? crew.name;
    const template = getCrewTemplate(crew.crewId);
    const requestedGates = template?.reviewGates ?? [];
    const gateRationales = {
        pm: {
            owner,
            rationale: 'Scope, audience, and success criteria review.',
            applies: context.signals.pmFacing,
        },
        architecture: {
            owner,
            rationale: 'Interface, scope, and fallback review before execution.',
            applies: context.signals.multiFile || context.signals.technical || context.signals.memorySensitive || context.signals.orchestrationSensitive,
        },
        'code-review': {
            owner: 'Code Review Layer',
            rationale: 'Implementation review after verification evidence is available.',
            applies: context.signals.technical || context.signals.implementation,
        },
        cto: {
            owner: 'CTO Review Layer',
            rationale: 'Release-readiness and system-quality review.',
            applies: context.signals.release || context.signals.multiFile || context.signals.memorySensitive || context.signals.orchestrationSensitive,
        },
        devops: {
            owner: 'DevOps Shipper',
            rationale: 'Packaging, release, deployment, and rollback review.',
            applies: context.signals.release || context.signals.devopsFacing,
        },
        'marketer-docs': {
            owner: 'Marketer Docs Layer',
            rationale: 'Website, README, docs, and launch-facing surfacing review.',
            applies: context.signals.docsOrPitch,
        },
    };
    return requestedGates
        .filter((gate) => gateRationales[gate]?.applies)
        .map((gate) => ({
        gate,
        status: 'planned',
        owner: gateRationales[gate].owner,
        rationale: gateRationales[gate].rationale,
    }));
}
function analyzeTaskContext(goal, matchedDomains, files = []) {
    const haystack = `${goal} ${(files || []).join(' ')} ${matchedDomains.join(' ')}`.toLowerCase();
    const technical = /(dashboard|frontend|backend|api|runtime|control plane|orchestrat|memory|mcp|token|repo|project|scope|bug|fix|refactor|implement|patch|review|audit|cto|typescript|react|node|test)/.test(haystack);
    const implementation = /(implement|fix|patch|refactor|upgrade|repair|remediate|rewrite|build)/.test(haystack) || technical;
    const multiFile = (files || []).length >= 3;
    const memorySensitive = /(memory|recall|profile|scope|quarantine|compaction|knowledge)/.test(haystack);
    const orchestrationSensitive = /(orchestrat|skill|workflow|hook|automation|parallel|worker|planner|crew|specialist|gate)/.test(haystack);
    const release = /(release|deploy|shipping|rollback|package|ci\/cd|ci|infra|environment|production)/.test(haystack);
    const devopsFacing = /(deploy|docker|kubernetes|release|infra|pipeline|workflow|github action|build)/.test(haystack);
    const docsOrPitch = /(docs|readme|website|landing|investor|pitch|launch|demo|marketing)/.test(haystack);
    const pmFacing = /(plan|planning|product|roadmap|prd|requirements|user research|persona|investor|pitch)/.test(haystack);
    return {
        technical,
        implementation,
        multiFile,
        memorySensitive,
        orchestrationSensitive,
        release,
        devopsFacing,
        docsOrPitch,
        pmFacing,
    };
}
function isCrossDomainSpecialist(specialist) {
    return ['marketing', 'paid-media', 'sales', 'support', 'strategy', 'project-management'].includes(specialist.division);
}
function rankAssetSelectors(goal, matchedDomains, files, values, optimizationProfile) {
    const signals = analyzeTaskContext(goal, matchedDomains, files);
    const requestSet = new Set(values.map((value) => value.toLowerCase()));
    const ranked = dedupeStrings(values)
        .map((value) => {
        const lower = value.toLowerCase();
        let score = 0.1;
        if (requestSet.has(lower))
            score += 0.5;
        if (signals.technical && /(memory|backend|frontend|workflow|hook|automation|api|security|test|token|orchestrat|runtime)/.test(lower))
            score += 0.32;
        if (signals.docsOrPitch && /(docs|content|landing|pitch|investor|launch)/.test(lower))
            score += 0.24;
        if (signals.technical && /(marketing|seo|campaign|social|sales|gtm|product|pdlc)/.test(lower))
            score -= 0.58;
        if (!signals.docsOrPitch && /(marketing|seo|campaign|social|sales|gtm)/.test(lower))
            score -= 0.24;
        return { value, score };
    })
        .filter((entry) => entry.score > 0.18)
        .sort((left, right) => right.score - left.score || left.value.localeCompare(right.value));
    return ranked
        .slice(0, optimizationProfile === 'max' ? 6 : 4)
        .map((entry) => entry.value);
}
function pinRequestedSelectors(requested, ranked) {
    return dedupeStrings([
        ...requested,
        ...ranked,
    ]);
}
function dedupeStrings(values) {
    return [...new Set(values.filter(Boolean))];
}
// ─── Curated starter roster ─────────────────────────────────────────────────
/**
 * The 10 curated starter specialists for the "Your next 10 hires" empty state.
 * Ordered by recommended first-hire priority. These IDs are stable and map
 * directly to entries in src/engines/data/specialist-profiles.json.
 */
export const STARTER_SPECIALIST_IDS = [
    'specialist_engineering-engineering-rapid-prototyper',
    'specialist_engineering-engineering-senior-developer',
    'specialist_engineering-engineering-backend-architect',
    'specialist_engineering-engineering-frontend-developer',
    'specialist_design-design-ux-architect',
    'specialist_engineering-engineering-security-engineer',
    'specialist_engineering-engineering-devops-automator',
    'specialist_engineering-engineering-technical-writer',
    'specialist_engineering-engineering-incident-response-commander',
    'specialist_engineering-engineering-ai-engineer',
];
/**
 * Returns the curated list of 10 starter specialists in priority order.
 * Any ID not found in the live catalog is silently skipped so catalog drift
 * can't break the dashboard empty state.
 */
export function listStarterSpecialists() {
    const all = ensureSpecialists();
    const byId = new Map(all.map((s) => [s.specialistId, s]));
    return STARTER_SPECIALIST_IDS
        .map((id) => byId.get(id))
        .filter((s) => s !== undefined);
}
