import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { join } from "node:path";
import type { AgentTool } from "@jaeswift/jae-agent-core";
import { type Static, Type } from "@sinclair/typebox";

const MEMORY_DIR = join(homedir(), ".jae", "memory");
const MEMORY_FILE = join(MEMORY_DIR, "memories.json");

interface MemoryEntry {
	id: string;
	content: string;
	tags: string[];
	createdAt: string;
}

function loadMemories(): MemoryEntry[] {
	if (!existsSync(MEMORY_FILE)) return [];
	try {
		return JSON.parse(readFileSync(MEMORY_FILE, "utf-8")) as MemoryEntry[];
	} catch {
		return [];
	}
}

function saveMemories(entries: MemoryEntry[]): void {
	if (!existsSync(MEMORY_DIR)) mkdirSync(MEMORY_DIR, { recursive: true });
	writeFileSync(MEMORY_FILE, JSON.stringify(entries, null, 2));
}

function scoreMatch(entry: MemoryEntry, query: string): number {
	const q = query.toLowerCase();
	const text = (entry.content + " " + entry.tags.join(" ")).toLowerCase();
	const words = q.split(/\s+/);
	return words.filter((w) => text.includes(w)).length / Math.max(words.length, 1);
}

const memorySchema = Type.Object({
	action: Type.Union([Type.Literal("save"), Type.Literal("recall"), Type.Literal("list"), Type.Literal("delete")], {
		description: "save: store info | recall: search by query | list: show all | delete: remove by id",
	}),
	content: Type.Optional(Type.String({ description: "Content to save (required for save action)" })),
	query: Type.Optional(Type.String({ description: "Search query (required for recall action)" })),
	tags: Type.Optional(Type.Array(Type.String(), { description: "Tags for categorisation (optional for save)" })),
	id: Type.Optional(Type.String({ description: "Memory ID (required for delete action)" })),
	limit: Type.Optional(Type.Number({ description: "Max results to return for recall (default: 5)" })),
});

export type MemoryToolInput = Static<typeof memorySchema>;

export interface MemoryToolDetails {
	action: string;
	count?: number;
	id?: string;
}

export const memoryTool: AgentTool<typeof memorySchema, MemoryToolDetails> = {
	name: "memory",
	label: "Memory",
	description:
		"Persistent memory across sessions. Save facts, recall by query, list all memories, or delete by ID. Stored in ~/.jae/memory/.",
	parameters: memorySchema,
	async execute(toolCallId, params, signal) {
		const { action, content, query, tags = [], id, limit = 5 } = params;
		const memories = loadMemories();

		if (action === "save") {
			if (!content)
				return {
					content: [{ type: "text", text: "Error: content is required for save action" }],
					details: { action },
				};
			const entry: MemoryEntry = {
				id: `mem-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
				content,
				tags,
				createdAt: new Date().toISOString(),
			};
			memories.push(entry);
			saveMemories(memories);
			return {
				content: [{ type: "text", text: `Memory saved. ID: ${entry.id}` }],
				details: { action, id: entry.id },
			};
		}

		if (action === "recall") {
			if (!query)
				return {
					content: [{ type: "text", text: "Error: query is required for recall action" }],
					details: { action },
				};
			const scored = memories
				.map((m) => ({ m, score: scoreMatch(m, query) }))
				.filter((x) => x.score > 0)
				.sort((a, b) => b.score - a.score)
				.slice(0, limit)
				.map((x) => x.m);
			const text =
				scored.length === 0
					? `No memories found for: ${query}`
					: scored
							.map((m) => `[${m.id}] ${m.content}${m.tags.length ? ` (tags: ${m.tags.join(", ")})` : ""}`)
							.join("\n\n");
			return { content: [{ type: "text", text }], details: { action, count: scored.length } };
		}

		if (action === "list") {
			const text =
				memories.length === 0
					? "No memories stored."
					: memories
							.map((m) => `[${m.id}] ${m.content.slice(0, 100)}${m.content.length > 100 ? "..." : ""}`)
							.join("\n");
			return { content: [{ type: "text", text }], details: { action, count: memories.length } };
		}

		if (action === "delete") {
			if (!id)
				return {
					content: [{ type: "text", text: "Error: id is required for delete action" }],
					details: { action },
				};
			const filtered = memories.filter((m) => m.id !== id);
			if (filtered.length === memories.length)
				return { content: [{ type: "text", text: `No memory found with ID: ${id}` }], details: { action } };
			saveMemories(filtered);
			return { content: [{ type: "text", text: `Memory ${id} deleted.` }], details: { action, id } };
		}

		return { content: [{ type: "text", text: `Unknown action: ${action}` }], details: { action } };
	},
};
