import { getLogger } from "../../../../logger.js";
import { SENTINEL_KEY } from "../../constants/artifact-syntax.js";
import { formatOversizedRetrievalReason } from "../../artifacts/artifact-utils.js";
import { getModelAwareCompressionConfig } from "../../compression/BaseCompressor.js";
import { agentSessionManager } from "../../session/AgentSession.js";
import { wrapToolWithStreaming } from "./tool-wrapper.js";
import { z } from "@hono/zod-openapi";
import { tool } from "ai";

//#region src/domains/run/agents/tools/default-tools.ts
const logger = getLogger("Agent");
function getArtifactTools(ctx) {
	return tool({
		description: "Retrieves the complete data of an existing artifact. NOTE: To pass an artifact to a tool you do NOT need to call this — use the { \"$artifact\": \"id\", \"$tool\": \"toolCallId\" } sentinel and the system resolves the full data automatically. summary_data in available_artifacts already contains all preview fields. Only call this when you specifically need the actual value of a non-preview field that is not visible in summary_data.",
		inputSchema: z.object({
			artifactId: z.string().describe("The unique identifier of the artifact to get."),
			toolCallId: z.string().describe("The tool call ID associated with this artifact.")
		}),
		execute: async ({ artifactId, toolCallId }) => {
			logger.info({
				artifactId,
				toolCallId
			}, "get_artifact_full executed");
			const compressor = ctx.currentCompressor;
			if (compressor?.hasSummarizedArtifact(artifactId)) {
				const summarized = compressor.getSummarizedArtifact(artifactId);
				logger.info({
					artifactId,
					toolCallId
				}, "Blocked retrieval of artifact already summarized in compression");
				return {
					artifactId,
					status: "already_summarized",
					key_findings: summarized?.key_findings ?? [],
					hint: `This artifact's key findings are already in your compressed context. Use them directly to answer. To pass this artifact to a tool, use { "${SENTINEL_KEY.ARTIFACT}": "${artifactId}", "${SENTINEL_KEY.TOOL}": "${summarized?.tool_call_id ?? toolCallId}" } sentinel instead of retrieving it.`
				};
			}
			const streamRequestId = ctx.streamRequestId ?? "";
			const artifactService = agentSessionManager.getArtifactService(streamRequestId);
			if (!artifactService) throw new Error(`ArtifactService not found for session ${streamRequestId}`);
			const artifactData = await artifactService.getArtifactFull(artifactId, toolCallId);
			if (!artifactData) throw new Error(`Artifact ${artifactId} with toolCallId ${toolCallId} not found`);
			if (artifactData.metadata?.isOversized || artifactData.metadata?.retrievalBlocked) {
				logger.info({
					artifactId,
					toolCallId,
					tokenSize: artifactData.metadata?.originalTokenSize,
					contextWindowSize: artifactData.metadata?.contextWindowSize
				}, "Blocked retrieval of oversized artifact");
				return {
					artifactId: artifactData.artifactId,
					name: artifactData.name,
					description: artifactData.description,
					type: artifactData.type,
					status: "retrieval_blocked",
					warning: "⚠️ This artifact contains an oversized tool result that cannot be retrieved to prevent context overflow.",
					reason: formatOversizedRetrievalReason(artifactData.metadata?.originalTokenSize || 0, artifactData.metadata?.contextWindowSize || 0),
					toolInfo: {
						toolName: artifactData.metadata?.toolName,
						toolArgs: artifactData.metadata?.toolArgs,
						structureInfo: artifactData.data?._structureInfo
					},
					recommendation: "The tool arguments that caused this large result are included above. Consider: 1) Using more specific filters/queries with the original tool, 2) Asking the user to break down the request, 3) Processing the data differently."
				};
			}
			return {
				artifactId: artifactData.artifactId,
				name: artifactData.name,
				description: artifactData.description,
				type: artifactData.type,
				data: artifactData.data
			};
		}
	});
}
function createLoadSkillTool(ctx) {
	return tool({
		description: "Load an on-demand skill by name and return its full content plus any attached files so you can apply it in this conversation.",
		inputSchema: z.object({ name: z.string().describe("The skill name from the on-demand skills list.") }),
		execute: async ({ name }) => {
			const skill = ctx.config.skills?.find((item) => item.name === name);
			if (!skill) throw new Error(`Skill ${name} not found`);
			return {
				id: skill.id,
				name: skill.name,
				description: skill.description,
				content: skill.content,
				files: skill.files ?? []
			};
		}
	});
}
async function getDefaultTools(ctx, streamRequestId) {
	const defaultTools = {};
	const compressionConfig = getModelAwareCompressionConfig();
	if (await agentHasArtifactComponents(ctx) || compressionConfig.enabled) defaultTools.get_reference_artifact = getArtifactTools(ctx);
	if (ctx.config.skills?.some((skill) => !skill.alwaysLoaded)) defaultTools.load_skill = wrapToolWithStreaming(ctx, "load_skill", createLoadSkillTool(ctx), streamRequestId, "tool");
	logger.info({
		agentId: ctx.config.id,
		streamRequestId
	}, "Adding compress_context tool to defaultTools");
	defaultTools.compress_context = tool({
		description: "Manually compress the current conversation context to save space. Use when shifting topics, completing major tasks, or when context feels cluttered.",
		inputSchema: z.object({ reason: z.string().describe("Why you are requesting compression (e.g., \"shifting from research to coding\", \"completed analysis phase\")") }),
		execute: async ({ reason }) => {
			logger.info({
				agentId: ctx.config.id,
				streamRequestId,
				reason
			}, "Manual compression requested by LLM");
			if (ctx.currentCompressor) ctx.currentCompressor.requestManualCompression(reason);
			return {
				status: "compression_requested",
				reason,
				message: "Context compression will be applied on the next generation step. Previous work has been summarized and saved as artifacts."
			};
		}
	});
	logger.info("getDefaultTools returning tools:", Object.keys(defaultTools).join(", "));
	return defaultTools;
}
async function agentHasArtifactComponents(ctx) {
	try {
		const subAgents = ctx.executionContext.project.agents[ctx.config.agentId]?.subAgents;
		if (!subAgents) return false;
		return Object.values(subAgents).some((subAgent) => (subAgent.artifactComponents?.length ?? 0) > 0);
	} catch (error) {
		logger.error({
			error,
			agentId: ctx.config.agentId
		}, "Failed to check agent artifact components");
		return false;
	}
}

//#endregion
export { agentHasArtifactComponents, createLoadSkillTool, getArtifactTools, getDefaultTools };