/**
 * Maps Drips protocol state to rFlows FlowNode[] for visualization.
 * Pure functions — no side effects, no network calls.
 */

import type { FlowNode, SourceNodeData, FunnelNodeData, OutcomeNodeData, DripsNodeConfig, SpendingAllocation } from './types';
import type { DripsAccountState, DripsStream, DripsSplit } from './drips-client';
import { dripsAmtToMonthly } from './drips-client';

// Common token decimals
const TOKEN_DECIMALS: Record<string, number> = {
	'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': 6,  // USDC (mainnet)
	'0xdac17f958d2ee523a2206206994597c13d831ec7': 6,  // USDT (mainnet)
	'0x6b175474e89094c44da98b954eedeac495271d0f': 18, // DAI (mainnet)
	'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': 18, // WETH (mainnet)
};

function getTokenDecimals(tokenAddress: string): number {
	return TOKEN_DECIMALS[tokenAddress.toLowerCase()] ?? 18;
}

function getTokenSymbol(tokenAddress: string): string {
	const symbols: Record<string, string> = {
		'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48': 'USDC',
		'0xdac17f958d2ee523a2206206994597c13d831ec7': 'USDT',
		'0x6b175474e89094c44da98b954eedeac495271d0f': 'DAI',
		'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': 'WETH',
	};
	return symbols[tokenAddress.toLowerCase()] ?? 'TOKEN';
}

const SPENDING_COLORS = ['#3b82f6', '#8b5cf6', '#ec4899', '#06b6d4', '#10b981', '#6366f1'];

export interface MapDripsOptions {
	/** Origin offset for layout positioning */
	originX?: number;
	originY?: number;
	/** USD price per token (for rate conversion). Default 1 (stablecoins). */
	usdPrice?: number;
}

/**
 * Map DripsAccountState into FlowNode[] for rFlows canvas.
 *
 * Layout (3-tier grid):
 *   Row 0: Source nodes (senders streaming to this address)
 *   Row 1: Funnel node (the imported address itself)
 *   Row 2: Outcome nodes (split receivers)
 */
export function mapDripsToFlowNodes(state: DripsAccountState, options: MapDripsOptions = {}): FlowNode[] {
	const { originX = 100, originY = 100, usdPrice = 1 } = options;
	const nodes: FlowNode[] = [];
	const now = Date.now();
	const addr = state.address.toLowerCase();
	const funnelId = `drips-${addr}`;

	// ── Row 0: Source nodes (incoming streams grouped by sender) ──

	const bySender = new Map<string, DripsStream[]>();
	for (const s of state.incomingStreams) {
		if (s.isPaused) continue;
		const key = s.sender.toLowerCase();
		if (!bySender.has(key)) bySender.set(key, []);
		bySender.get(key)!.push(s);
	}

	let srcIdx = 0;
	for (const [sender, streams] of bySender) {
		const totalRate = streams.reduce((sum, s) => {
			const dec = getTokenDecimals(s.tokenAddress);
			return sum + dripsAmtToMonthly(s.amtPerSec, dec, usdPrice);
		}, 0);

		const shortAddr = `${sender.slice(0, 6)}...${sender.slice(-4)}`;
		const tokens = [...new Set(streams.map(s => getTokenSymbol(s.tokenAddress)))].join('/');
		const sourceId = `drips-src-${sender}`;

		const dripsConfig: DripsNodeConfig = {
			chainId: state.chainId,
			address: sender,
			streamIds: streams.map(s => s.id),
			importedAt: now,
		};

		nodes.push({
			id: sourceId,
			type: 'source',
			position: { x: originX + srcIdx * 280, y: originY },
			data: {
				label: `${shortAddr} (${tokens})`,
				flowRate: Math.round(totalRate * 100) / 100,
				sourceType: 'safe_wallet',
				targetAllocations: [{
					targetId: funnelId,
					percentage: 100,
					color: '#10b981',
				}],
				dripsConfig,
			} as SourceNodeData,
		});
		srcIdx++;
	}

	// ── Row 1: Funnel node (the imported address) ──

	const totalInflow = nodes.reduce((sum, n) => {
		if (n.type === 'source') return sum + (n.data as SourceNodeData).flowRate;
		return sum;
	}, 0);

	// Build spending allocations from splits
	const spendingAllocations: SpendingAllocation[] = state.splits.map((split, i) => {
		const receiverAddr = split.receiver.toLowerCase();
		return {
			targetId: `drips-out-${receiverAddr}`,
			percentage: (split.weight / 1_000_000) * 100,
			color: SPENDING_COLORS[i % SPENDING_COLORS.length],
		};
	});

	// Unallocated remainder (no splits receiver) stays in funnel
	const totalSplitPct = spendingAllocations.reduce((s, a) => s + a.percentage, 0);

	const funnelConfig: DripsNodeConfig = {
		chainId: state.chainId,
		address: state.address,
		importedAt: now,
	};

	nodes.push({
		id: funnelId,
		type: 'funnel',
		position: { x: originX + Math.max(0, (srcIdx - 1)) * 140, y: originY + 250 },
		data: {
			label: `${state.address.slice(0, 6)}...${state.address.slice(-4)}`,
			currentValue: 0,
			overflowThreshold: totalInflow * 3, // 3 months buffer
			capacity: totalInflow * 6,
			inflowRate: totalInflow,
			drainRate: totalInflow * (totalSplitPct / 100),
			overflowAllocations: [],
			spendingAllocations,
			dripsConfig: funnelConfig,
		} as FunnelNodeData,
	});

	// ── Row 2: Outcome nodes (split receivers) ──

	state.splits.forEach((split, i) => {
		const receiverAddr = split.receiver.toLowerCase();
		const outcomeId = `drips-out-${receiverAddr}`;
		const shortAddr = `${receiverAddr.slice(0, 6)}...${receiverAddr.slice(-4)}`;
		const pct = (split.weight / 1_000_000) * 100;
		const monthlyAmount = totalInflow * (pct / 100);

		const dripsConfig: DripsNodeConfig = {
			chainId: state.chainId,
			address: split.receiver,
			splitWeight: split.weight,
			splitIndex: i,
			importedAt: now,
		};

		nodes.push({
			id: outcomeId,
			type: 'outcome',
			position: { x: originX + i * 280, y: originY + 500 },
			data: {
				label: `${shortAddr} (${pct.toFixed(1)}%)`,
				description: `Drips split receiver — ${pct.toFixed(1)}% of incoming`,
				fundingReceived: 0,
				fundingTarget: monthlyAmount * 12, // annualized target
				status: 'in-progress',
				dripsConfig,
			} as OutcomeNodeData,
		});
	});

	return nodes;
}
