<template>
	<NewModal
		ref="withdrawModal"
		:closable="currentStage !== 'completion'"
		:hide-header="currentStage === 'completion'"
		:merge-header="currentStage === 'completion'"
		:scrollable="true"
		max-content-height="72vh"
		:on-hide="onModalHide"
	>
		<template #title>
			<div v-if="shouldShowTitle" class="flex flex-wrap items-center gap-1 text-secondary">
				<template v-if="currentStage === 'tax-form'">
					<span class="text-lg font-bold text-contrast sm:text-xl">{{
						formatMessage(messages.taxFormStage)
					}}</span>
				</template>
				<template v-else-if="currentStage === 'method-selection'">
					<span class="text-lg font-bold text-contrast sm:text-xl">{{
						formatMessage(messages.methodSelectionStage)
					}}</span>
					<ChevronRightIcon class="size-5 text-secondary" stroke-width="3" />
					<span class="text-lg text-secondary sm:text-xl">{{
						formatMessage(messages.detailsLabel)
					}}</span>
				</template>
				<template v-else-if="isDetailsStage">
					<button
						class="active:scale-9 bg-transparent p-0 text-lg text-secondary transition-colors duration-200 hover:text-primary sm:text-xl"
						@click="goToBreadcrumbStage('method-selection')"
					>
						{{ formatMessage(messages.methodSelectionStage) }}
					</button>
					<ChevronRightIcon class="size-5 text-secondary" stroke-width="3" />
					<span class="text-lg font-bold text-contrast sm:text-xl">{{
						formatMessage(messages.detailsLabel)
					}}</span>
				</template>
			</div>
		</template>
		<div class="mx-auto w-full max-w-[496px] sm:mx-0 sm:min-w-[496px]">
			<TaxFormStage
				v-if="currentStage === 'tax-form'"
				:balance="balance"
				:on-show-tax-form="showTaxFormModal"
			/>
			<MethodSelectionStage
				v-else-if="currentStage === 'method-selection'"
				:on-show-tax-form="showTaxFormModal"
				@close-modal="withdrawModal?.hide()"
			/>
			<TremendousDetailsStage v-else-if="currentStage === 'tremendous-details'" />
			<MuralpayKycStage v-else-if="currentStage === 'muralpay-kyc'" />
			<MuralpayDetailsStage v-else-if="currentStage === 'muralpay-details'" />
			<LegacyPaypalDetailsStage v-else-if="currentStage === 'paypal-details'" />
			<CompletionStage v-else-if="currentStage === 'completion'" />
			<div v-else>Something went wrong</div>
		</div>
		<template #actions>
			<div v-if="currentStage === 'completion'" class="mt-4 flex w-full gap-3">
				<ButtonStyled class="flex-1">
					<button class="w-full text-contrast" @click="handleClose">
						{{ formatMessage(messages.closeButton) }}
					</button>
				</ButtonStyled>
				<ButtonStyled class="flex-1">
					<button class="w-full text-contrast" @click="handleViewTransactions">
						{{ formatMessage(messages.transactionsButton) }}
					</button>
				</ButtonStyled>
			</div>
			<div v-else class="mt-4 flex flex-col justify-end gap-2 sm:flex-row">
				<ButtonStyled type="outlined">
					<button
						class="!border-surface-5"
						:disabled="leftButtonConfig.disabled"
						@click="leftButtonConfig.handler"
					>
						<component :is="leftButtonConfig.icon" />
						{{ leftButtonConfig.label }}
					</button>
				</ButtonStyled>
				<ButtonStyled :color="rightButtonConfig.color">
					<button :disabled="rightButtonConfig.disabled" @click="rightButtonConfig.handler">
						<component
							:is="rightButtonConfig.icon"
							v-if="rightButtonConfig.iconPosition === 'before'"
							:class="rightButtonConfig.iconClass"
						/>
						{{ rightButtonConfig.label }}
						<component
							:is="rightButtonConfig.icon"
							v-if="rightButtonConfig.iconPosition === 'after'"
							:class="rightButtonConfig.iconClass"
						/>
					</button>
				</ButtonStyled>
			</div>
		</template>
	</NewModal>
	<CreatorTaxFormModal
		ref="taxFormModal"
		close-button-text="Continue"
		@success="onTaxFormSuccess"
		@cancelled="onTaxFormCancelled"
	/>
</template>

<script setup lang="ts">
import {
	ArrowLeftRightIcon,
	ChevronRightIcon,
	FileTextIcon,
	LeftArrowIcon,
	RightArrowIcon,
	SpinnerIcon,
	XIcon,
} from '@modrinth/assets'
import {
	ButtonStyled,
	commonMessages,
	defineMessages,
	injectNotificationManager,
	NewModal,
	useVIntl,
} from '@modrinth/ui'
import { computed, nextTick, onMounted, ref, useTemplateRef, watch } from 'vue'

import {
	createWithdrawContext,
	type PaymentProvider,
	type PayoutMethod,
	provideWithdrawContext,
	TAX_THRESHOLD_ACTUAL,
	type WithdrawStage,
} from '@/providers/creator-withdraw.ts'

import CreatorTaxFormModal from './CreatorTaxFormModal.vue'
import CompletionStage from './withdraw-stages/CompletionStage.vue'
import LegacyPaypalDetailsStage from './withdraw-stages/LegacyPaypalDetailsStage.vue'
import MethodSelectionStage from './withdraw-stages/MethodSelectionStage.vue'
import MuralpayDetailsStage from './withdraw-stages/MuralpayDetailsStage.vue'
import MuralpayKycStage from './withdraw-stages/MuralpayKycStage.vue'
import TaxFormStage from './withdraw-stages/TaxFormStage.vue'
import TremendousDetailsStage from './withdraw-stages/TremendousDetailsStage.vue'

type FormCompletionStatus = 'unknown' | 'unrequested' | 'unsigned' | 'tin-mismatch' | 'complete'

interface UserBalanceResponse {
	available: number
	withdrawn_lifetime: number
	withdrawn_ytd: number
	pending: number
	dates: Record<string, number>
	requested_form_type: string | null
	form_completion_status: FormCompletionStatus | null
}

const props = defineProps<{
	balance: UserBalanceResponse | null
	preloadedPaymentData?: { country: string; methods: PayoutMethod[] } | null
}>()

const emit = defineEmits<{
	(e: 'refresh-data' | 'hide'): void
}>()

const withdrawModal = useTemplateRef<InstanceType<typeof NewModal>>('withdrawModal')
const taxFormModal = ref<InstanceType<typeof CreatorTaxFormModal> | null>(null)
const isSubmitting = ref(false)

function show(preferred?: WithdrawStage) {
	if (preferred) {
		setStage(preferred, true)
		withdrawModal.value?.show()
		return
	}

	const firstStage = stages.value[0]
	if (firstStage) {
		setStage(firstStage, true)
	}

	withdrawModal.value?.show()
}

defineExpose({
	show,
})

const { formatMessage } = useVIntl()
const { addNotification } = injectNotificationManager()

const withdrawContext = createWithdrawContext(
	props.balance,
	props.preloadedPaymentData || undefined,
)
provideWithdrawContext(withdrawContext)

const {
	currentStage,
	previousStep,
	nextStep,
	canProceed,
	setStage,
	withdrawData,
	resetData,
	stages,
	submitWithdrawal,
	restoreStateFromStorage,
	clearSavedState,
} = withdrawContext

watch(
	() => props.balance,
	(newBalance) => {
		if (newBalance) {
			withdrawContext.balance.value = newBalance
		}
	},
	{ deep: true },
)

onMounted(() => {
	const route = useRoute()
	const router = useRouter()

	if (route.query.paypal_auth_return === 'true') {
		const savedState = restoreStateFromStorage()

		if (savedState?.data) {
			withdrawData.value = { ...savedState.data }

			nextTick(() => {
				show(savedState.stage)
			})

			clearSavedState()
		}

		const query = { ...route.query }
		delete query.paypal_auth_return
		router.replace({ query })
	}
})

const needsTaxForm = computed(() => {
	if (!props.balance || currentStage.value !== 'tax-form') return false
	const ytd = props.balance.withdrawn_ytd ?? 0
	const available = props.balance.available ?? 0
	const status = props.balance.form_completion_status
	return status !== 'complete' && ytd + available >= 600
})

const remainingLimit = computed(() => {
	if (!props.balance) return 0
	const ytd = props.balance.withdrawn_ytd ?? 0
	const raw = TAX_THRESHOLD_ACTUAL - ytd
	if (raw <= 0) return 0
	const cents = Math.floor(raw * 100)
	return cents / 100
})

const leftButtonConfig = computed(() => {
	if (previousStep.value) {
		return {
			icon: LeftArrowIcon,
			label: formatMessage(commonMessages.backButton),
			handler: () => setStage(previousStep.value, true),
			disabled: isSubmitting.value,
		}
	}
	return {
		icon: XIcon,
		label: formatMessage(commonMessages.cancelButton),
		handler: () => withdrawModal.value?.hide(),
		disabled: isSubmitting.value,
	}
})

const rightButtonConfig = computed(() => {
	const stage = currentStage.value
	const isTaxFormStage = stage === 'tax-form'
	const isDetailsStage =
		stage === 'muralpay-details' || stage === 'tremendous-details' || stage === 'paypal-details'

	if (isTaxFormStage && needsTaxForm.value && remainingLimit.value > 0) {
		return {
			icon: RightArrowIcon,
			label: formatMessage(messages.continueWithLimit),
			handler: continueWithLimit,
			disabled: false,
			color: 'standard' as const,
			iconPosition: 'after' as const,
		}
	}

	if (isTaxFormStage && needsTaxForm.value) {
		return {
			icon: FileTextIcon,
			label: formatMessage(messages.completeTaxForm),
			handler: showTaxFormModal,
			disabled: false,
			color: 'orange' as const,
			iconPosition: 'before' as const,
		}
	}

	if (isDetailsStage) {
		return {
			icon: isSubmitting.value ? SpinnerIcon : ArrowLeftRightIcon,
			iconClass: isSubmitting.value ? 'animate-spin' : undefined,
			label: formatMessage(messages.withdrawButton),
			handler: handleWithdraw,
			disabled: !canProceed.value || isSubmitting.value,
			color: 'brand' as const,
			iconPosition: 'before' as const,
		}
	}

	return {
		icon: RightArrowIcon,
		label: formatMessage(commonMessages.nextButton),
		handler: () => setStage(nextStep.value),
		disabled: !canProceed.value,
		color: 'standard' as const,
		iconPosition: 'after' as const,
	}
})

function continueWithLimit() {
	withdrawData.value.tax.skipped = true
	setStage(nextStep.value)
}

function buildSupportData(error: any): Record<string, unknown> {
	// Extract response headers, excluding sensitive ones
	const responseHeaders: Record<string, string> = {}
	if (error?.response?.headers) {
		const headers = error.response.headers
		const entries =
			typeof headers.entries === 'function' ? [...headers.entries()] : Object.entries(headers)
		for (const [key, value] of entries) {
			const lowerKey = key.toLowerCase()
			// Exclude sensitive headers
			if (!['authorization', 'cookie', 'set-cookie'].includes(lowerKey)) {
				responseHeaders[key] = value
			}
		}
	}

	return {
		timestamp: new Date().toISOString(),
		provider: withdrawData.value.selection.provider,
		method: withdrawData.value.selection.method,
		methodId: withdrawData.value.selection.methodId,
		country: withdrawData.value.selection.country?.id,
		amount: withdrawData.value.calculation?.amount,
		fee: withdrawData.value.calculation?.fee,
		request: {
			url: 'POST /api/v3/payout',
		},
		response: {
			status: error?.response?.status ?? error?.statusCode,
			statusText: error?.response?.statusText,
			headers: responseHeaders,
			body: error?.data,
		},
	}
}

function formatBilingualText(
	messageDescriptor: { id: string; defaultMessage: string },
	values?: Record<string, string>,
): string {
	const localized = formatMessage(messageDescriptor, values)
	// Interpolate values into the English default message
	let english = messageDescriptor.defaultMessage
	if (values) {
		for (const [key, value] of Object.entries(values)) {
			english = english.replace(`{${key}}`, value)
		}
	}

	if (localized === english) {
		return localized
	}

	return `${localized}\n${english}`
}

function getWithdrawalError(
	error: any,
	provider: PaymentProvider | null,
): { title: string; text: string; supportData: Record<string, unknown> } {
	const description = error?.data?.description?.toLowerCase() || ''
	const supportData = buildSupportData(error)

	// === Common patterns (all providers) ===
	// Tax form error
	if (description.includes('tax form')) {
		return {
			title: formatMessage(messages.errorTaxFormTitle),
			text: formatBilingualText(messages.errorTaxFormText),
			supportData,
		}
	}

	// Minimum amount not met
	if (
		description.includes('payoutminimumnotmeterror') ||
		description.includes('minimum') ||
		(description.includes('amount') && description.includes('less'))
	) {
		return {
			title: formatMessage(messages.errorMinimumNotMetTitle),
			text: formatBilingualText(messages.errorMinimumNotMetText),
			supportData,
		}
	}

	// Insufficient balance
	if (description.includes('enough funds') || description.includes('insufficient')) {
		return {
			title: formatMessage(messages.errorInsufficientBalanceTitle),
			text: formatBilingualText(messages.errorInsufficientBalanceText),
			supportData,
		}
	}

	// Email verification required
	if (description.includes('verify your email') || description.includes('email_verified')) {
		return {
			title: formatMessage(messages.errorEmailVerificationTitle),
			text: formatBilingualText(messages.errorEmailVerificationText),
			supportData,
		}
	}

	// === MuralPay-only patterns ===
	if (provider === 'muralpay') {
		// Invalid crypto wallet address
		if (
			(description.includes('wallet') && description.includes('invalid')) ||
			description.includes('wallet_address') ||
			(description.includes('blockchain') && description.includes('invalid'))
		) {
			return {
				title: formatMessage(messages.errorInvalidWalletTitle),
				text: formatBilingualText(messages.errorInvalidWalletText),
				supportData,
			}
		}

		// Invalid bank details
		if (
			(description.includes('bank') || description.includes('account')) &&
			(description.includes('invalid') || description.includes('failed'))
		) {
			return {
				title: formatMessage(messages.errorInvalidBankTitle),
				text: formatBilingualText(messages.errorInvalidBankText),
				supportData,
			}
		}

		// Invalid/fraudulent address (physical address for KYC)
		if (
			description.includes('address') &&
			(description.includes('invalid') ||
				description.includes('verification') ||
				description.includes('fraudulent'))
		) {
			return {
				title: formatMessage(messages.errorInvalidAddressTitle),
				text: formatBilingualText(messages.errorInvalidAddressText),
				supportData,
			}
		}
	}

	// === PayPal/Venmo-only patterns ===
	if (provider === 'paypal' || provider === 'venmo') {
		// Account not linked
		if (
			description.includes('not linked') ||
			description.includes('link') ||
			description.includes('paypal account') ||
			description.includes('venmo')
		) {
			return {
				title: formatMessage(messages.errorAccountNotLinkedTitle),
				text: formatBilingualText(messages.errorAccountNotLinkedText),
				supportData,
			}
		}

		// Country mismatch for PayPal
		if (
			provider === 'paypal' &&
			(description.includes('us paypal') || description.includes('international paypal'))
		) {
			return {
				title: formatMessage(messages.errorPaypalCountryMismatchTitle),
				text: formatBilingualText(messages.errorPaypalCountryMismatchText),
				supportData,
			}
		}
	}

	// Generic fallback
	const errorDescription = error?.data?.description || ''
	return {
		title: formatMessage(messages.errorGenericTitle),
		text: formatBilingualText(messages.errorGenericText, { error: errorDescription }),
		supportData,
	}
}

async function handleWithdraw() {
	if (isSubmitting.value) return

	try {
		isSubmitting.value = true
		await submitWithdrawal()
		setStage('completion')
	} catch (error) {
		console.error('Withdrawal failed:', error)

		const { title, text, supportData } = getWithdrawalError(
			error,
			withdrawData.value.selection.provider,
		)
		addNotification({
			title,
			text,
			type: 'error',
			supportData,
		})
	} finally {
		isSubmitting.value = false
	}
}

const shouldShowTitle = computed(() => {
	return currentStage.value !== 'completion'
})

const isDetailsStage = computed(() => {
	const detailsStages: WithdrawStage[] = [
		'tremendous-details',
		'muralpay-kyc',
		'muralpay-details',
		'paypal-details',
	]
	const current = currentStage.value
	return current ? detailsStages.includes(current) : false
})

function showTaxFormModal(e?: MouseEvent) {
	withdrawModal.value?.hide()
	taxFormModal.value?.startTaxForm(e ?? new MouseEvent('click'))
}

function onTaxFormSuccess() {
	emit('refresh-data')
	nextTick(() => {
		show('method-selection')
	})
}

function onTaxFormCancelled() {
	show('tax-form')
}

function onModalHide() {
	resetData()
	emit('hide')
}

function goToBreadcrumbStage(stage: WithdrawStage) {
	setStage(stage, true)
}

function handleClose() {
	withdrawModal.value?.hide()
	emit('refresh-data')
}

function handleViewTransactions() {
	withdrawModal.value?.hide()
	navigateTo('/dashboard/revenue/transfers')
}

const messages = defineMessages({
	taxFormStage: {
		id: 'dashboard.creator-withdraw-modal.stage.tax-form',
		defaultMessage: 'Tax form',
	},
	methodSelectionStage: {
		id: 'dashboard.creator-withdraw-modal.stage.method-selection',
		defaultMessage: 'Method',
	},
	tremendousDetailsStage: {
		id: 'dashboard.creator-withdraw-modal.stage.tremendous-details',
		defaultMessage: 'Details',
	},
	muralpayKycStage: {
		id: 'dashboard.creator-withdraw-modal.stage.muralpay-kyc',
		defaultMessage: 'Verification',
	},
	muralpayDetailsStage: {
		id: 'dashboard.creator-withdraw-modal.stage.muralpay-details',
		defaultMessage: 'Account Details',
	},
	completionStage: {
		id: 'dashboard.creator-withdraw-modal.stage.completion',
		defaultMessage: 'Complete',
	},
	detailsLabel: {
		id: 'dashboard.creator-withdraw-modal.details-label',
		defaultMessage: 'Details',
	},
	completeTaxForm: {
		id: 'dashboard.creator-withdraw-modal.complete-tax-form',
		defaultMessage: 'Complete tax form',
	},
	continueWithLimit: {
		id: 'dashboard.creator-withdraw-modal.continue-with-limit',
		defaultMessage: 'Continue with limit',
	},
	withdrawButton: {
		id: 'dashboard.creator-withdraw-modal.withdraw-button',
		defaultMessage: 'Withdraw',
	},
	closeButton: {
		id: 'dashboard.withdraw.completion.close-button',
		defaultMessage: 'Close',
	},
	transactionsButton: {
		id: 'dashboard.withdraw.completion.transactions-button',
		defaultMessage: 'Transactions',
	},
	errorTaxFormTitle: {
		id: 'dashboard.withdraw.error.tax-form.title',
		defaultMessage: 'Please complete tax form',
	},
	errorTaxFormText: {
		id: 'dashboard.withdraw.error.tax-form.text',
		defaultMessage: 'You must complete a tax form to submit your withdrawal request.',
	},
	errorInvalidWalletTitle: {
		id: 'dashboard.withdraw.error.invalid-wallet.title',
		defaultMessage: 'Invalid wallet address',
	},
	errorInvalidWalletText: {
		id: 'dashboard.withdraw.error.invalid-wallet.text',
		defaultMessage:
			'The crypto wallet address you provided is invalid. Please double-check and try again.',
	},
	errorInvalidBankTitle: {
		id: 'dashboard.withdraw.error.invalid-bank.title',
		defaultMessage: 'Invalid bank details',
	},
	errorInvalidBankText: {
		id: 'dashboard.withdraw.error.invalid-bank.text',
		defaultMessage:
			'The bank account details you provided are invalid. Please verify your information.',
	},
	errorInvalidAddressTitle: {
		id: 'dashboard.withdraw.error.invalid-address.title',
		defaultMessage: 'Address verification failed',
	},
	errorInvalidAddressText: {
		id: 'dashboard.withdraw.error.invalid-address.text',
		defaultMessage:
			'The address you provided could not be verified. Please check your address details.',
	},
	errorMinimumNotMetTitle: {
		id: 'dashboard.withdraw.error.minimum-not-met.title',
		defaultMessage: 'Amount too low',
	},
	errorMinimumNotMetText: {
		id: 'dashboard.withdraw.error.minimum-not-met.text',
		defaultMessage:
			"The withdrawal amount (after fees) doesn't meet the minimum requirement. Please increase your withdrawal amount.",
	},
	errorGenericTitle: {
		id: 'dashboard.withdraw.error.generic.title',
		defaultMessage: 'Unable to withdraw',
	},
	errorGenericText: {
		id: 'dashboard.withdraw.error.generic.text',
		defaultMessage:
			'We were unable to submit your withdrawal request, please check your details or contact support.\n{error}',
	},
	errorInsufficientBalanceTitle: {
		id: 'dashboard.withdraw.error.insufficient-balance.title',
		defaultMessage: 'Insufficient balance',
	},
	errorInsufficientBalanceText: {
		id: 'dashboard.withdraw.error.insufficient-balance.text',
		defaultMessage: 'You do not have enough funds to make this withdrawal.',
	},
	errorEmailVerificationTitle: {
		id: 'dashboard.withdraw.error.email-verification.title',
		defaultMessage: 'Email verification required',
	},
	errorEmailVerificationText: {
		id: 'dashboard.withdraw.error.email-verification.text',
		defaultMessage: 'You must verify your email address before withdrawing funds.',
	},
	errorAccountNotLinkedTitle: {
		id: 'dashboard.withdraw.error.account-not-linked.title',
		defaultMessage: 'Account not linked',
	},
	errorAccountNotLinkedText: {
		id: 'dashboard.withdraw.error.account-not-linked.text',
		defaultMessage: 'Please link your payment account before withdrawing.',
	},
	errorPaypalCountryMismatchTitle: {
		id: 'dashboard.withdraw.error.paypal-country-mismatch.title',
		defaultMessage: 'PayPal region mismatch',
	},
	errorPaypalCountryMismatchText: {
		id: 'dashboard.withdraw.error.paypal-country-mismatch.text',
		defaultMessage:
			'Please use the correct PayPal transfer option for your region (US or International).',
	},
})
</script>
