<template>
	<div :class="$style.container">
		<div :class="$style.logoContainer">
			<Logo />
		</div>
		<n8n-card>
			<div :class="$style.headerContainer">
				<n8n-heading size="xlarge" color="text-dark">{{
					showRecoveryCodeForm
						? i18.baseText('mfa.recovery.modal.title')
						: i18.baseText('mfa.code.modal.title')
				}}</n8n-heading>
			</div>
			<div :class="[$style.formContainer, reportError ? $style.formError : '']">
				<n8n-form-inputs
					v-if="formInputs"
					data-test-id="mfa-login-form"
					:inputs="formInputs"
					:event-bus="formBus"
					@input="onInput"
					@submit="onSubmit"
				/>
				<div :class="$style.infoBox">
					<n8n-text
						v-if="!showRecoveryCodeForm && !reportError"
						size="small"
						color="text-base"
						:bold="false"
						>{{ i18.baseText('mfa.code.input.info') }}
						<a data-test-id="mfa-enter-recovery-code-button" @click="onRecoveryCodeClick">{{
							i18.baseText('mfa.code.input.info.action')
						}}</a></n8n-text
					>
					<n8n-text v-if="reportError" color="danger" size="small"
						>{{ formError }}
						<a
							v-if="!showRecoveryCodeForm"
							:class="$style.recoveryCodeLink"
							@click="onRecoveryCodeClick"
						>
							{{ i18.baseText('mfa.recovery.input.info.action') }}</a
						>
					</n8n-text>
				</div>
			</div>
			<div>
				<n8n-button
					float="right"
					:loading="verifyingMfaToken"
					:label="
						showRecoveryCodeForm
							? i18.baseText('mfa.recovery.button.verify')
							: i18.baseText('mfa.code.button.continue')
					"
					size="large"
					:disabled="!hasAnyChanges"
					@click="onSaveClick"
				/>
				<n8n-button
					float="left"
					:label="i18.baseText('mfa.button.back')"
					size="large"
					type="tertiary"
					@click="onBackClick"
				/>
			</div>
		</n8n-card>
	</div>
</template>

<script setup lang="ts">
import type { IFormInputs } from '@/Interface';
import Logo from '../components/Logo.vue';
import {
	MFA_AUTHENTICATION_RECOVERY_CODE_INPUT_MAX_LENGTH,
	MFA_AUTHENTICATION_TOKEN_INPUT_MAX_LENGTH,
	MFA_FORM,
} from '@/constants';
import { mfaEventBus } from '@/event-bus';
import { onMounted, ref } from 'vue';
import { useI18n } from '@/composables/useI18n';
import { toRefs } from '@vueuse/core';

// ---------------------------------------------------------------------------
// #region Props
// ---------------------------------------------------------------------------

const props = defineProps<{
	reportError: boolean;
}>();

// #endregion

// ---------------------------------------------------------------------------
// #region Reactive properties
// ---------------------------------------------------------------------------

const hasAnyChanges = ref(false);
const formBus = ref(mfaEventBus);
const formInputs = ref<null | IFormInputs>(null);
const showRecoveryCodeForm = ref(false);
const verifyingMfaToken = ref(false);
const formError = ref('');
const { reportError } = toRefs(props);

// ---------------------------------------------------------------------------
// #region Composable
// ---------------------------------------------------------------------------

const i18 = useI18n();

// #endregion

// ---------------------------------------------------------------------------
// #region Emit
// ---------------------------------------------------------------------------

const emit = defineEmits<{
	onFormChanged: [formField: string];
	onBackClick: [formField: string];
	submit: [{ token: string; recoveryCode: string }];
}>();

// #endregion

// ---------------------------------------------------------------------------
// #region Methods
// ---------------------------------------------------------------------------

const formField = (
	name: string,
	label: string,
	placeholder: string,
	maxlength: number,
	focus = true,
) => {
	return {
		name,
		initialValue: '',
		properties: {
			label,
			placeholder,
			maxlength,
			capitalize: true,
			validateOnBlur: false,
			focusInitially: focus,
		},
	};
};

const onRecoveryCodeClick = () => {
	formError.value = '';
	showRecoveryCodeForm.value = true;
	hasAnyChanges.value = false;
	formInputs.value = [mfaRecoveryCodeFieldWithDefaults()];
	emit('onFormChanged', MFA_FORM.MFA_RECOVERY_CODE);
};

const onBackClick = () => {
	if (!showRecoveryCodeForm.value) {
		emit('onBackClick', MFA_FORM.MFA_TOKEN);
		return;
	}

	showRecoveryCodeForm.value = false;
	hasAnyChanges.value = true;
	formInputs.value = [mfaTokenFieldWithDefaults()];
	emit('onBackClick', MFA_FORM.MFA_RECOVERY_CODE);
};

const onSubmit = async (form: { token: string; recoveryCode: string }) => {
	formError.value = !showRecoveryCodeForm.value
		? i18.baseText('mfa.code.invalid')
		: i18.baseText('mfa.recovery.invalid');
	emit('submit', form);
};

const onInput = ({ target: { value, name } }: { target: { value: string; name: string } }) => {
	const isSubmittingMfaToken = name === 'token';
	const inputValidLength = isSubmittingMfaToken
		? MFA_AUTHENTICATION_TOKEN_INPUT_MAX_LENGTH
		: MFA_AUTHENTICATION_RECOVERY_CODE_INPUT_MAX_LENGTH;

	if (value.length !== inputValidLength) {
		hasAnyChanges.value = false;
		return;
	}

	verifyingMfaToken.value = true;
	hasAnyChanges.value = true;

	const dataToSubmit = isSubmittingMfaToken
		? { token: value, recoveryCode: '' }
		: { token: '', recoveryCode: value };

	onSubmit(dataToSubmit)
		.catch(() => {})
		.finally(() => (verifyingMfaToken.value = false));
};

const mfaRecoveryCodeFieldWithDefaults = () => {
	return formField(
		'recoveryCode',
		i18.baseText('mfa.recovery.input.label'),
		i18.baseText('mfa.recovery.input.placeholder'),
		MFA_AUTHENTICATION_RECOVERY_CODE_INPUT_MAX_LENGTH,
	);
};

const mfaTokenFieldWithDefaults = () => {
	return formField(
		'token',
		i18.baseText('mfa.code.input.label'),
		i18.baseText('mfa.code.input.placeholder'),
		MFA_AUTHENTICATION_TOKEN_INPUT_MAX_LENGTH,
	);
};

const onSaveClick = () => {
	formBus.value.emit('submit');
};

// #endregion

// ---------------------------------------------------------------------------
// #region Lifecycle hooks
// ---------------------------------------------------------------------------

onMounted(() => {
	formInputs.value = [mfaTokenFieldWithDefaults()];
});

// #endregion
</script>

<style lang="scss" module>
body {
	background-color: var(--color-background-light);
}

.container {
	display: flex;
	align-items: center;
	flex-direction: column;
	padding-top: var(--spacing-2xl);

	> * {
		margin-bottom: var(--spacing-l);
		width: 352px;
	}
}

.logoContainer {
	display: flex;
	justify-content: center;
}

.formContainer {
	padding-bottom: var(--spacing-xl);
}

.headerContainer {
	text-align: center;
	margin-bottom: var(--spacing-xl);
}

.formError input {
	border-color: var(--color-danger);
}

.recoveryCodeLink {
	text-decoration: underline;
}

.infoBox {
	padding-top: var(--spacing-4xs);
}
</style>
