/**
 * MCP tools for rWallet (read-only).
 *
 * Tools: rwallet_get_safe_balances, rwallet_get_transfers,
 *        rwallet_get_defi_positions, rwallet_get_crdt_balances
 */

import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { enrichWithPrices } from "../../modules/rwallet/lib/price-feed";
import { getDefiPositions } from "../../modules/rwallet/lib/defi-positions";
import { getTokenDoc, listTokenDocs, getAllBalances } from "../token-service";
import { verifyToken } from "../auth";

// Safe Global chain prefix map (subset for MCP)
const CHAIN_MAP: Record<string, string> = {
	"1": "mainnet",
	"10": "optimism",
	"100": "gnosis",
	"137": "polygon",
	"8453": "base",
	"42161": "arbitrum",
	"42220": "celo",
	"43114": "avalanche",
	"56": "bsc",
	"324": "zksync",
	"11155111": "sepolia",
};

const NATIVE_TOKENS: Record<string, { name: string; symbol: string; decimals: number }> = {
	"1": { name: "Ether", symbol: "ETH", decimals: 18 },
	"10": { name: "Ether", symbol: "ETH", decimals: 18 },
	"100": { name: "xDAI", symbol: "xDAI", decimals: 18 },
	"137": { name: "MATIC", symbol: "MATIC", decimals: 18 },
	"8453": { name: "Ether", symbol: "ETH", decimals: 18 },
	"42161": { name: "Ether", symbol: "ETH", decimals: 18 },
};

function safeApiBase(prefix: string): string {
	return `https://api.safe.global/tx-service/${prefix}/api/v1`;
}

export function registerWalletTools(server: McpServer) {
	server.tool(
		"rwallet_get_safe_balances",
		"Get token balances for a Safe wallet address on a specific chain, enriched with USD prices",
		{
			chain_id: z.string().describe("Chain ID (e.g. '1' for Ethereum, '100' for Gnosis, '8453' for Base)"),
			address: z.string().describe("Wallet address (0x...)"),
		},
		async ({ chain_id, address }) => {
			const prefix = CHAIN_MAP[chain_id];
			if (!prefix) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Unsupported chain", supported: Object.keys(CHAIN_MAP) }) }] };
			}

			try {
				const res = await fetch(
					`${safeApiBase(prefix)}/safes/${address}/balances/?trusted=true&exclude_spam=true`,
					{ signal: AbortSignal.timeout(15000) },
				);
				if (!res.ok) {
					return { content: [{ type: "text", text: JSON.stringify({ error: `Safe API error: ${res.status}` }) }] };
				}

				const raw = await res.json() as any[];
				const nativeToken = NATIVE_TOKENS[chain_id] || { name: "ETH", symbol: "ETH", decimals: 18 };
				const data = raw.map((item: any) => ({
					tokenAddress: item.tokenAddress,
					token: item.token || nativeToken,
					balance: item.balance || "0",
					fiatBalance: item.fiatBalance || "0",
					fiatConversion: item.fiatConversion || "0",
				}));

				const enriched = (await enrichWithPrices(data, chain_id, { filterSpam: true }))
					.filter(b => BigInt(b.balance || "0") > 0n);

				return { content: [{ type: "text", text: JSON.stringify(enriched, null, 2) }] };
			} catch (e: any) {
				return { content: [{ type: "text", text: JSON.stringify({ error: e.message }) }] };
			}
		},
	);

	server.tool(
		"rwallet_get_transfers",
		"Get recent token transfers for a Safe wallet address",
		{
			chain_id: z.string().describe("Chain ID"),
			address: z.string().describe("Wallet address (0x...)"),
			limit: z.number().optional().describe("Max results (default 20)"),
		},
		async ({ chain_id, address, limit }) => {
			const prefix = CHAIN_MAP[chain_id];
			if (!prefix) {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Unsupported chain" }) }] };
			}

			try {
				const maxResults = Math.min(limit || 20, 100);
				const res = await fetch(
					`${safeApiBase(prefix)}/safes/${address}/all-transactions/?limit=${maxResults}&executed=true`,
					{ signal: AbortSignal.timeout(15000) },
				);
				if (!res.ok) {
					return { content: [{ type: "text", text: JSON.stringify({ error: `Safe API error: ${res.status}` }) }] };
				}

				const data = await res.json() as any;
				return { content: [{ type: "text", text: JSON.stringify(data.results || [], null, 2) }] };
			} catch (e: any) {
				return { content: [{ type: "text", text: JSON.stringify({ error: e.message }) }] };
			}
		},
	);

	server.tool(
		"rwallet_get_defi_positions",
		"Get DeFi protocol positions (Aave, Uniswap, etc.) for an address via Zerion",
		{
			address: z.string().describe("Wallet address (0x...)"),
		},
		async ({ address }) => {
			const positions = await getDefiPositions(address);
			return { content: [{ type: "text", text: JSON.stringify(positions, null, 2) }] };
		},
	);

	server.tool(
		"rwallet_get_crdt_balances",
		"Get CRDT token balances (cUSDC, $MYCO) for all holders or a specific DID (requires auth token)",
		{
			token: z.string().describe("JWT auth token (required — exposes DID→balance mapping)"),
			did: z.string().optional().describe("Filter by DID (optional — returns all holders if omitted)"),
		},
		async ({ token, did }) => {
			try {
				await verifyToken(token);
			} catch {
				return { content: [{ type: "text", text: JSON.stringify({ error: "Invalid or expired token" }) }], isError: true };
			}

			const docIds = listTokenDocs();
			const result: Array<{
				tokenId: string;
				name: string;
				symbol: string;
				decimals: number;
				holders: Record<string, { did: string; label: string; balance: number }>;
			}> = [];

			for (const docId of docIds) {
				const tokenId = docId.replace("global:tokens:ledgers:", "");
				const doc = getTokenDoc(tokenId);
				if (!doc) continue;

				let holders = getAllBalances(doc);
				if (did) {
					const filtered: typeof holders = {};
					if (holders[did]) filtered[did] = holders[did];
					holders = filtered;
				}

				result.push({
					tokenId,
					name: doc.token.name,
					symbol: doc.token.symbol,
					decimals: doc.token.decimals,
					holders,
				});
			}

			return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
		},
	);
}
