export interface RibbitConnectTokens {
	customerToken: string
	accountToken: string
	institutionName: string
	accountMask: string
	bankId?: number | string
}

function parseEventRecord(eventData: unknown): Record<string, unknown> {
	if (typeof eventData === 'string') {
		try {
			return JSON.parse(eventData) as Record<string, unknown>
		} catch {
			return {}
		}
	}

	return (eventData ?? {}) as Record<string, unknown>
}

function readField(record: Record<string, unknown>, ...keys: string[]): unknown {
	for (const key of keys) {
		const value = record[key]
		if (value !== undefined && value !== null && value !== '') {
			return value
		}
	}

	const normalizedKeys = new Set(keys.map((key) => key.toLowerCase()))
	for (const [key, value] of Object.entries(record)) {
		if (normalizedKeys.has(key.toLowerCase()) && value !== undefined && value !== null && value !== '') {
			return value
		}
	}

	return undefined
}

/**
 * Normalize CONNECT account mask values to the last 4 digits.
 */
export function extractAccountLast4(value: unknown): string {
	if (value === undefined || value === null) {
		return ''
	}

	const raw = String(value).trim()
	if (!raw) {
		return ''
	}

	if (/^\d{4}$/.test(raw)) {
		return raw
	}

	const trailingDigits = raw.match(/(\d{4})\D*$/)
	if (trailingDigits) {
		return trailingDigits[1]
	}

	const digitsOnly = raw.replace(/\D/g, '')
	if (digitsOnly.length >= 4) {
		return digitsOnly.slice(-4)
	}

	return ''
}

function resolveAccountRecord(
	data: Record<string, unknown>,
	enrollment: Record<string, unknown>,
): Record<string, unknown> {
	const accounts = [data.accounts, enrollment.accounts].find(Array.isArray) as unknown[] | undefined
	const accountFromList =
		accounts?.find((entry) => entry && typeof entry === 'object') ?? undefined

	return {
		...(accountFromList && typeof accountFromList === 'object'
			? (accountFromList as Record<string, unknown>)
			: {}),
		...((data.selectedAccount ?? enrollment.selectedAccount ?? {}) as Record<string, unknown>),
		...((data.account ?? enrollment.account ?? {}) as Record<string, unknown>),
	}
}

function resolveAccountMask(
	data: Record<string, unknown>,
	enrollment: Record<string, unknown>,
	account: Record<string, unknown>,
): string {
	const candidates = [
		readField(account, 'last4Digits', 'last4digits', 'last4', 'lastFour', 'lastFourDigits', 'mask'),
		readField(account, 'accountNumber', 'accountNumberMask', 'displayAccountNumber', 'accountMask'),
		readField(data, 'last4Digits', 'last4digits', 'last4', 'lastFour', 'lastFourDigits', 'mask', 'accountMask'),
		readField(enrollment, 'last4Digits', 'last4digits', 'last4', 'lastFour', 'lastFourDigits', 'mask', 'accountMask'),
		readField(data, 'accountNumber', 'accountNumberMask', 'displayAccountNumber'),
		readField(enrollment, 'accountNumber', 'accountNumberMask', 'displayAccountNumber'),
	]

	for (const candidate of candidates) {
		const last4 = extractAccountLast4(candidate)
		if (last4) {
			return last4
		}
	}

	return ''
}

/**
 * Parse Ribbit CONNECT completion payload defensively.
 * Matches Green Club RibbitWidget parsing (nested account + enrollment shapes).
 */
export function parseConnectEventData(eventData: unknown): RibbitConnectTokens {
	const raw = parseEventRecord(eventData)
	const data = (raw.data ?? raw.payload ?? raw) as Record<string, unknown>
	const enrollment = (data.enrollmentData ?? data.enrollment ?? data) as Record<string, unknown>
	const account = resolveAccountRecord(data, enrollment)

	const customerToken = String(
		readField(data, 'customerId', 'customerToken') ??
			readField(enrollment, 'customerId', 'customerToken') ??
			readField(account, 'customerId', 'customerToken') ??
			'',
	)

	const accountToken = String(
		readField(data, 'accountToken', 'accountId') ??
			readField(enrollment, 'accountToken', 'accountId') ??
			readField(account, 'accountToken', 'accountId', 'id') ??
			'',
	)

	const institutionName = String(
		readField(account, 'bankName', 'institutionName') ??
			readField(data, 'institutionName', 'bankName') ??
			readField(enrollment, 'institutionName', 'bankName') ??
			'',
	)

	const accountMask = resolveAccountMask(data, enrollment, account)

	const bankIdRaw =
		readField(account, 'bankId', 'BankId') ??
		readField(data, 'bankId', 'BankId') ??
		readField(enrollment, 'bankId', 'BankId')

	const bankId =
		bankIdRaw !== undefined && bankIdRaw !== null && String(bankIdRaw).trim() !== ''
			? bankIdRaw
			: undefined

	return { customerToken, accountToken, institutionName, accountMask, bankId }
}
