import { z } from "zod";
import type { db as prodDb } from "../../../db/index.ts";
import { withImageUrls } from "../../services/storage.service.ts";
import {
	createCandidate,
	createThread,
	deleteCandidate,
	getAllThreads,
	getThreadWithCandidates,
	resolveThread,
	updateCandidate,
} from "../../services/thread.service.ts";

type Db = typeof prodDb;

interface ToolResult {
	content: Array<{ type: "text"; text: string }>;
}

function textResult(data: unknown): ToolResult {
	return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
}

function errorResult(message: string): ToolResult {
	return {
		content: [{ type: "text", text: JSON.stringify({ error: message }) }],
	};
}

export const threadToolDefinitions = [
	{
		name: "list_threads",
		description:
			"List research threads. Threads are the recommended way to evaluate gear purchases — each thread tracks multiple candidates for a single gear slot, making it easy to compare options before committing.",
		inputSchema: {
			includeResolved: z
				.boolean()
				.optional()
				.describe(
					"Include resolved threads (default: false, only active threads)",
				),
		},
	},
	{
		name: "get_thread",
		description:
			"Get a thread with all its candidates for detailed comparison.",
		inputSchema: {
			id: z.number().describe("Thread ID"),
		},
	},
	{
		name: "create_thread",
		description:
			"Start a new research thread for a gear slot. This is the preferred workflow: create a thread, add candidates with pros/cons/prices, compare them, then resolve the thread to add the winner to your collection.",
		inputSchema: {
			name: z.string().describe("Thread name (e.g. 'Handlebar bag')"),
			categoryId: z.number().describe("Category ID"),
		},
	},
	{
		name: "resolve_thread",
		description:
			"Resolve a research thread by picking the winning candidate. The winner is automatically added to the gear collection as a new item, and the thread is marked as resolved.",
		inputSchema: {
			threadId: z.number().describe("Thread ID"),
			candidateId: z.number().describe("ID of the winning candidate"),
		},
	},
	{
		name: "add_candidate",
		description: "Add a candidate option to a research thread for comparison.",
		inputSchema: {
			threadId: z.number().describe("Thread ID"),
			name: z.string().describe("Candidate name"),
			categoryId: z.number().describe("Category ID"),
			weightGrams: z.number().optional().describe("Weight in grams"),
			priceCents: z.number().optional().describe("Price in cents"),
			notes: z.string().optional().describe("Notes"),
			productUrl: z.string().optional().describe("Product URL"),
			imageFilename: z.string().optional().describe("Image filename"),
			pros: z.string().optional().describe("Pros of this candidate"),
			cons: z.string().optional().describe("Cons of this candidate"),
		},
	},
	{
		name: "update_candidate",
		description:
			"Update a candidate's details (name, price, pros, cons, etc.).",
		inputSchema: {
			id: z.number().describe("Candidate ID"),
			name: z.string().optional().describe("Candidate name"),
			weightGrams: z.number().optional().describe("Weight in grams"),
			priceCents: z.number().optional().describe("Price in cents"),
			categoryId: z.number().optional().describe("Category ID"),
			notes: z.string().optional().describe("Notes"),
			productUrl: z.string().optional().describe("Product URL"),
			imageFilename: z.string().optional().describe("Image filename"),
			imageSourceUrl: z.string().optional().describe("Image source URL"),
			status: z
				.string()
				.optional()
				.describe("Status: researching, ordered, or arrived"),
			pros: z.string().optional().describe("Pros"),
			cons: z.string().optional().describe("Cons"),
		},
	},
	{
		name: "remove_candidate",
		description: "Remove a candidate from a research thread.",
		inputSchema: {
			id: z.number().describe("Candidate ID to remove"),
		},
	},
];

export function registerThreadTools(db: Db, userId: number) {
	return {
		list_threads: async (args: {
			includeResolved?: boolean;
		}): Promise<ToolResult> => {
			try {
				const threadList = await getAllThreads(
					db,
					userId,
					args.includeResolved ?? false,
				);
				return textResult(threadList);
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		get_thread: async (args: { id: number }): Promise<ToolResult> => {
			try {
				const thread = await getThreadWithCandidates(db, userId, args.id);
				if (!thread) return errorResult(`Thread ${args.id} not found`);
				const enrichedCandidates = await withImageUrls(thread.candidates);
				return textResult({ ...thread, candidates: enrichedCandidates });
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		create_thread: async (args: {
			name: string;
			categoryId: number;
		}): Promise<ToolResult> => {
			try {
				const thread = await createThread(db, userId, args);
				return textResult(thread);
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		resolve_thread: async (args: {
			threadId: number;
			candidateId: number;
		}): Promise<ToolResult> => {
			try {
				const result = await resolveThread(
					db,
					userId,
					args.threadId,
					args.candidateId,
				);
				if (!result.success) {
					return errorResult(result.error ?? "Failed to resolve thread");
				}
				return textResult(result);
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		add_candidate: async (args: {
			threadId: number;
			name: string;
			categoryId: number;
			weightGrams?: number;
			priceCents?: number;
			notes?: string;
			productUrl?: string;
			imageFilename?: string;
			pros?: string;
			cons?: string;
		}): Promise<ToolResult> => {
			try {
				const { threadId, ...data } = args;
				const candidate = await createCandidate(db, userId, threadId, data);
				return textResult(candidate);
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		update_candidate: async (args: {
			id: number;
			name?: string;
			weightGrams?: number;
			priceCents?: number;
			categoryId?: number;
			notes?: string;
			productUrl?: string;
			imageFilename?: string;
			imageSourceUrl?: string;
			status?: "researching" | "ordered" | "arrived";
			pros?: string;
			cons?: string;
		}): Promise<ToolResult> => {
			try {
				const { id, ...data } = args;
				const candidate = await updateCandidate(db, userId, id, data);
				if (!candidate) return errorResult(`Candidate ${id} not found`);
				return textResult(candidate);
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},

		remove_candidate: async (args: { id: number }): Promise<ToolResult> => {
			try {
				const candidate = await deleteCandidate(db, userId, args.id);
				if (!candidate) return errorResult(`Candidate ${args.id} not found`);
				return textResult({ deleted: true, candidate });
			} catch (err) {
				return errorResult((err as Error).message);
			}
		},
	};
}
