import { analyzeFileInstrumentation } from "./validate-file.js";
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { basename, dirname, join, relative } from "node:path";

//#region src/mcp/scan-project.ts
const SOURCE_EXTENSIONS = new Set([
	".ts",
	".tsx",
	".js",
	".jsx"
]);
const SKIP_DIRS = new Set([
	"node_modules",
	"dist",
	".next",
	".git",
	"__tests__",
	"test"
]);
const MAX_DEPTH = 5;
const FRAMEWORK_DEPS = [
	["next", "nextjs"],
	["express", "express"],
	["fastify", "fastify"],
	["hono", "hono"],
	["@remix-run/node", "remix"]
];
const PROVIDER_DEPS = [
	"openai",
	"@anthropic-ai/sdk",
	"@google/generative-ai",
	"@google/genai",
	"@aws-sdk/client-bedrock-runtime",
	"@mistralai/mistralai",
	"@azure/openai",
	"cohere-ai"
];
const AGENT_FRAMEWORK_DEPS = [
	"langchain",
	"@langchain/core",
	"llamaindex",
	"@openai/agents",
	"crewai"
];
const MESSAGE_QUEUE_DEPS = [
	"bullmq",
	"bull",
	"ioredis",
	"amqplib",
	"@aws-sdk/client-sqs",
	"kafkajs",
	"@google-cloud/pubsub"
];
const FRONTEND_DEPS = [
	"react",
	"vue",
	"svelte",
	"@sveltejs/kit",
	"angular",
	"@angular/core"
];
const OTEL_DEPS = [
	"@opentelemetry/api",
	"@opentelemetry/sdk-trace-base",
	"@opentelemetry/sdk-trace-node",
	"@opentelemetry/exporter-trace-otlp-grpc",
	"@opentelemetry/exporter-trace-otlp-http",
	"@opentelemetry/exporter-trace-otlp-proto",
	"@opentelemetry/instrumentation",
	"@opentelemetry/auto-instrumentations-node",
	"@traceloop/node-server-sdk"
];
const STREAMING_RE = /streamText\s*\(|useChat\s*\(|stream\s*:\s*true/;
const VERCEL_AI_SDK_DEPS = [
	"ai",
	"@ai-sdk/openai",
	"@ai-sdk/anthropic",
	"@ai-sdk/google",
	"@ai-sdk/mistral"
];
const VERCEL_AI_SDK_RE = /\b(?:streamText|generateText|streamObject|generateObject|useChat|useCompletion|useAssistant)\s*\(/;
const EDGE_RUNTIME_RE = /runtime\s*=\s*['"]edge['"]/;
const ASSISTANTS_API_RE = /\.beta\.(?:threads|assistants)\./;
const LANGGRAPH_DEPS = ["@langchain/langgraph"];
const MULTI_AGENT_CODE_RE = /\.child\s*\(|\.runAs\s*\(|\.runAsSync\s*\(/;
const ROUTE_HANDLER_RE = /export\s+async\s+function\s+(?:POST|GET|PUT|DELETE)\b|app\.\s*(?:get|post|put|delete)\s*\(|router\./;
function collectSourceFiles(dir, rootPath, depth) {
	if (depth > MAX_DEPTH) return [];
	let entries;
	try {
		entries = readdirSync(dir);
	} catch {
		return [];
	}
	const files = [];
	for (const entry of entries) {
		if (SKIP_DIRS.has(entry)) continue;
		const fullPath = join(dir, entry);
		let stat;
		try {
			stat = statSync(fullPath);
		} catch {
			continue;
		}
		if (stat.isDirectory()) files.push(...collectSourceFiles(fullPath, rootPath, depth + 1));
		else if (stat.isFile()) {
			const ext = entry.slice(entry.lastIndexOf("."));
			if (SOURCE_EXTENSIONS.has(ext)) files.push(fullPath);
		}
	}
	return files;
}
function inferAgentId(filePath) {
	const base = basename(filePath).replace(/\.[^.]+$/, "");
	if (base === "route" || base === "index") return basename(dirname(filePath)) || base;
	return base;
}
function inferDescription(filePath, isRouteHandler) {
	const agentId = inferAgentId(filePath);
	if (isRouteHandler) return `Route handler agent: ${agentId}`;
	if (/agent|worker|service/i.test(filePath)) return `Background agent: ${agentId}`;
	return null;
}
function readPackageJson(rootPath) {
	const pkgPath = join(rootPath, "package.json");
	if (!existsSync(pkgPath)) return {
		name: null,
		allDeps: /* @__PURE__ */ new Set()
	};
	try {
		const raw = readFileSync(pkgPath, "utf8");
		const pkg = JSON.parse(raw);
		const name = typeof pkg.name === "string" ? pkg.name : null;
		const deps = pkg.dependencies ?? {};
		const devDeps = pkg.devDependencies ?? {};
		return {
			name,
			allDeps: new Set([...Object.keys(deps), ...Object.keys(devDeps)])
		};
	} catch {
		return {
			name: null,
			allDeps: /* @__PURE__ */ new Set()
		};
	}
}
function scanProject(rootPath) {
	const { name: projectName, allDeps } = readPackageJson(rootPath);
	let framework = null;
	for (const [dep, label] of FRAMEWORK_DEPS) if (allDeps.has(dep)) {
		framework = label;
		break;
	}
	const providers = PROVIDER_DEPS.filter((dep) => allDeps.has(dep));
	const agentFrameworks = AGENT_FRAMEWORK_DEPS.filter((dep) => allDeps.has(dep));
	let packageManager = null;
	if (existsSync(join(rootPath, "pnpm-lock.yaml"))) packageManager = "pnpm";
	else if (existsSync(join(rootPath, "yarn.lock"))) packageManager = "yarn";
	else if (existsSync(join(rootPath, "package-lock.json"))) packageManager = "npm";
	const isTypeScript = existsSync(join(rootPath, "tsconfig.json"));
	const hasAmplitudeAiDep = allDeps.has("@amplitude/ai");
	const sourceFiles = collectSourceFiles(rootPath, rootPath, 0);
	let totalCallSites = 0;
	let instrumentedCallSites = 0;
	let uninstrumentedCallSites = 0;
	let globalHasPatch = false;
	let globalHasSessionContext = false;
	let globalHasAmplitudeImport = hasAmplitudeAiDep;
	let hasStreaming = false;
	let hasVercelAiSdkUsage = false;
	let hasEdgeRuntime = false;
	let hasAssistantsApi = false;
	let hasMultiAgentCodePatterns = false;
	const globalWrappedProviders = /* @__PURE__ */ new Set();
	const CONSTRUCTOR_TO_PROVIDER = {
		OpenAI: "openai",
		Anthropic: "anthropic",
		Gemini: "gemini",
		GoogleGenerativeAI: "gemini",
		GoogleGenAI: "gemini",
		AzureOpenAI: "azure-openai",
		Bedrock: "bedrock",
		Mistral: "mistral",
		CohereClient: "cohere"
	};
	const agents = [];
	const filesWithCallSites = /* @__PURE__ */ new Set();
	const providerImportsPerFile = /* @__PURE__ */ new Map();
	for (const filePath of sourceFiles) {
		let content;
		try {
			content = readFileSync(filePath, "utf8");
		} catch {
			continue;
		}
		const analysis = analyzeFileInstrumentation(content);
		if (analysis.has_amplitude_import) globalHasAmplitudeImport = true;
		if (analysis.has_session_context) globalHasSessionContext = true;
		if (/\bpatch\s*\(\s*\{/.test(content)) globalHasPatch = true;
		if (STREAMING_RE.test(content) && analysis.total_call_sites > 0) hasStreaming = true;
		if (VERCEL_AI_SDK_RE.test(content)) hasVercelAiSdkUsage = true;
		if (EDGE_RUNTIME_RE.test(content)) hasEdgeRuntime = true;
		if (ASSISTANTS_API_RE.test(content)) hasAssistantsApi = true;
		if (MULTI_AGENT_CODE_RE.test(content)) hasMultiAgentCodePatterns = true;
		if (analysis.has_amplitude_import || /\bamplitude\s*:/.test(content)) {
			for (const m of content.matchAll(/new\s+(OpenAI|Anthropic|Gemini|AzureOpenAI|Bedrock|Mistral|GoogleGenerativeAI|GoogleGenAI|CohereClient)\s*\(/g)) {
				const provLabel = CONSTRUCTOR_TO_PROVIDER[m[1] ?? ""];
				if (provLabel && /\bamplitude\s*:/.test(content)) globalWrappedProviders.add(provLabel);
			}
			if (/\.wrap\s*\(/.test(content)) for (const site of analysis.call_sites) globalWrappedProviders.add(site.provider);
		}
		const fileProviders = /* @__PURE__ */ new Set();
		for (const site of analysis.call_sites) fileProviders.add(site.provider);
		if (fileProviders.size > 0) {
			const relPath = relative(rootPath, filePath);
			providerImportsPerFile.set(relPath, fileProviders);
		}
		if (analysis.total_call_sites > 0) {
			const relPath = relative(rootPath, filePath);
			filesWithCallSites.add(relPath);
			totalCallSites += analysis.total_call_sites;
			instrumentedCallSites += analysis.instrumented;
			uninstrumentedCallSites += analysis.uninstrumented;
			const isRouteHandler = ROUTE_HANDLER_RE.test(content);
			const inferredId = inferAgentId(filePath);
			const allInstrumented = analysis.uninstrumented === 0;
			agents.push({
				inferred_id: inferredId,
				file: relPath,
				call_sites: analysis.total_call_sites,
				is_instrumented: allInstrumented,
				is_route_handler: isRouteHandler,
				inferred_description: inferDescription(filePath, isRouteHandler),
				call_site_details: analysis.call_sites.map((cs) => ({
					line: cs.line,
					provider: cs.provider,
					api: cs.api,
					instrumented: cs.instrumented,
					containing_function: cs.containing_function,
					code_context: cs.code_context
				})),
				tool_definitions: analysis.tool_definitions,
				function_definitions: analysis.function_definitions
			});
		}
	}
	const globalHasWrappers = globalWrappedProviders.size > 0;
	if (globalHasWrappers && globalHasAmplitudeImport) {
		for (const agent of agents) if (!agent.is_instrumented) {
			if ([...new Set(agent.call_site_details.map((cs) => cs.provider))].every((p) => globalWrappedProviders.has(p))) {
				agent.is_instrumented = true;
				const delta = agent.call_sites;
				uninstrumentedCallSites -= delta;
				instrumentedCallSites += delta;
			}
		}
	}
	const multiAgentSignals = [];
	if (filesWithCallSites.size > 1) multiAgentSignals.push(`LLM call sites found in ${filesWithCallSites.size} separate files: ${[...filesWithCallSites].join(", ")}`);
	if (agentFrameworks.length > 0) multiAgentSignals.push(`Agent framework dependencies detected: ${agentFrameworks.join(", ")}`);
	const allProviders = /* @__PURE__ */ new Set();
	for (const provSet of providerImportsPerFile.values()) for (const p of provSet) allProviders.add(p);
	if (allProviders.size > 1) multiAgentSignals.push(`Multiple LLM providers in use: ${[...allProviders].join(", ")}`);
	for (const agent of agents) {
		const toolNames = agent.tool_definitions;
		const callFunctions = agent.call_site_details.map((cs) => cs.containing_function).filter(Boolean);
		const overlapping = toolNames.filter((t) => callFunctions.includes(t));
		if (overlapping.length > 0) multiAgentSignals.push(`File ${agent.file}: tool definitions ${overlapping.join(", ")} also contain LLM calls — possible delegation-as-tools pattern`);
	}
	if (hasMultiAgentCodePatterns) multiAgentSignals.push("Amplitude AI SDK delegation APIs detected: .child() or .runAs() — multi-agent confirmed");
	const isMultiAgent = hasMultiAgentCodePatterns;
	const messageQueueDeps = MESSAGE_QUEUE_DEPS.filter((dep) => allDeps.has(dep));
	const hasFrontendDeps = FRONTEND_DEPS.some((dep) => allDeps.has(dep));
	const otelPackages = OTEL_DEPS.filter((dep) => allDeps.has(dep));
	const hasOtelDeps = otelPackages.length > 0;
	const hasVercelAiSdk = VERCEL_AI_SDK_DEPS.some((dep) => allDeps.has(dep)) || hasVercelAiSdkUsage;
	const hasLanggraph = LANGGRAPH_DEPS.some((dep) => allDeps.has(dep));
	const recommendedTier = "advanced";
	const recommendations = [];
	if (hasStreaming) recommendations.push("Streaming detected: keep sessions open until stream is fully consumed. Use session.run() with an awaited stream, not a fire-and-forget pattern.");
	if (messageQueueDeps.length > 0) recommendations.push(`Message queue deps detected (${messageQueueDeps.join(", ")}): enable propagateContext in the bootstrap file and use injectContext/extractContext to correlate events across services.`);
	if (hasFrontendDeps) recommendations.push("Frontend framework detected alongside backend: pass browserSessionId and deviceId from frontend request headers to agent.session() for session replay linking.");
	if (hasVercelAiSdk) recommendations.push("Vercel AI SDK detected. Provider wrappers instrument the underlying provider SDK (openai, @anthropic-ai/sdk), not the Vercel AI SDK abstraction layer (streamText, generateText). If you also have the underlying provider as a direct dependency, wrappers will work because Vercel AI SDK delegates to them internally. Otherwise, use Tier 1 (patch) which intercepts at the transport level, or add the underlying provider SDK as a direct dependency.");
	if (hasEdgeRuntime) recommendations.push("Edge Runtime detected. session.run() relies on AsyncLocalStorage which may not be available in Edge Runtime or Cloudflare Workers. Use explicit context passing instead of session.run(): call agent.trackUserMessage() and agent.trackAiMessage() directly with sessionId parameter.");
	if (hasAssistantsApi) recommendations.push("OpenAI Assistants API detected (client.beta.threads/assistants). Provider wrappers do not auto-instrument the Assistants API. Use manual tracking with trackUserMessage/trackAiMessage, or migrate to the OpenAI Agents SDK which supports AmplitudeTracingProcessor.");
	if (hasLanggraph) recommendations.push("LangGraph detected. LLM calls within LangGraph are captured via the LangChain AmplitudeCallbackHandler, but graph orchestration events (node transitions, checkpoints, human-in-the-loop) are not yet instrumented.");
	if (hasOtelDeps) recommendations.push(`OpenTelemetry dependencies detected (${otelPackages.join(", ")}). Consider calling \`ai.enableOtel()\` after initializing AmplitudeAI to route all instrumentation through OTEL spans. The AmplitudeEventSpanProcessor maps GenAI semantic convention spans into [Agent] events automatically. Use \`observe({ type: "agent" })\` on orchestration functions to capture delegation latency and errors as real OTEL spans.`);
	if (globalHasAmplitudeImport && globalHasWrappers && globalHasSessionContext) recommendations.push("Project already has @amplitude/ai instrumentation with wrappers and session context. No re-instrumentation needed. Consider upgrading contentMode tiers, adding scoring, or expanding multi-agent coverage if applicable.");
	let hasFanOut = false;
	for (const agent of agents) {
		const fnCounts = /* @__PURE__ */ new Map();
		for (const cs of agent.call_site_details) {
			const fn = cs.containing_function;
			if (fn) fnCounts.set(fn, (fnCounts.get(fn) ?? 0) + 1);
		}
		const fanOutFns = [...fnCounts.entries()].filter(([, count]) => count >= 2).map(([fn]) => fn);
		if (fanOutFns.length > 0) {
			hasFanOut = true;
			multiAgentSignals.push(`File ${agent.file}: fan-out pattern detected — multiple LLM call sites in function(s): ${fanOutFns.join(", ")}. Use runAs() to delegate each parallel call to a child agent.`);
		}
	}
	if (hasFanOut) recommendations.push("Fan-out LLM pattern detected: one function dispatches multiple LLM calls in parallel. Use session.runAs(child, fn) for each sub-call so they share the parent traceId without emitting duplicate user messages. See Step 3g-b in the instrumentation guide (amplitude-ai.md).");
	return {
		project_name: projectName,
		runtime: "node",
		language: isTypeScript ? "typescript" : "javascript",
		framework,
		providers,
		agent_frameworks: agentFrameworks,
		package_manager: packageManager,
		existing_instrumentation: {
			has_amplitude_ai: globalHasAmplitudeImport,
			has_patch: globalHasPatch,
			has_wrappers: globalHasWrappers,
			has_session_context: globalHasSessionContext
		},
		agents,
		total_call_sites: totalCallSites,
		instrumented_call_sites: instrumentedCallSites,
		uninstrumented_call_sites: uninstrumentedCallSites,
		is_multi_agent: isMultiAgent,
		multi_agent_signals: multiAgentSignals,
		has_fan_out: hasFanOut,
		has_streaming: hasStreaming,
		has_vercel_ai_sdk: hasVercelAiSdk,
		has_edge_runtime: hasEdgeRuntime,
		has_assistants_api: hasAssistantsApi,
		has_langgraph: hasLanggraph,
		message_queue_deps: messageQueueDeps,
		has_frontend_deps: hasFrontendDeps,
		has_otel_deps: hasOtelDeps,
		otel_packages: otelPackages,
		recommendations,
		recommended_tier: recommendedTier
	};
}

//#endregion
export { scanProject };
//# sourceMappingURL=scan-project.js.map