<script setup lang="ts">
import { useConsentStore } from '@/app/stores/consent.store';
import { useDocumentTitle } from '@/app/composables/useDocumentTitle';
import { useI18n } from '@n8n/i18n';
import { onMounted, computed, ref, watch } from 'vue';
import type { ConsentDetails } from '@n8n/rest-api-client/api/consent';
import {
	N8nButton,
	N8nCallout,
	N8nCheckbox,
	N8nHeading,
	N8nIcon,
	N8nLogo,
	N8nNotice,
	N8nText,
} from '@n8n/design-system';
import { MCP_DOCS_PAGE_URL } from '@/features/ai/mcpAccess/mcp.constants';
import { useToast } from '@/app/composables/useToast';

const consentStore = useConsentStore();

const i18n = useI18n();
const documentTitle = useDocumentTitle();
const toast = useToast();

// Success state:
const waitingForRedirect = ref(false);
const redirectUriTrusted = ref(false);

const error = computed(() => consentStore.error);
const loading = computed(() => consentStore.isLoading);
const resourceName = computed(() => consentStore.consentDetails?.resourceName);

const errorMessage = computed(() => {
	if (consentStore.errorCode === 'resource_unavailable') {
		return i18n.baseText('oauth.consentView.error.resourceUnavailable');
	}
	return consentStore.error;
});

const clientDetails = computed<ConsentDetails | null>(() => consentStore.consentDetails);
const allowDisabled = computed(
	() => loading.value || error.value !== null || !clientDetails.value || !redirectUriTrusted.value,
);

watch(
	() => clientDetails.value?.redirectUri,
	() => {
		redirectUriTrusted.value = false;
	},
);

const handleAllow = async () => {
	try {
		const response = await consentStore.approveConsent(true);
		waitingForRedirect.value = true;
		window.location.href = response.redirectUrl;
	} catch (err) {
		toast.showError(err, i18n.baseText('oauth.consentView.error.allow'));
	}
};

const handleDeny = async () => {
	try {
		await consentStore.approveConsent(false);
		window.location.href = window.BASE_PATH ?? '/';
	} catch (err) {
		toast.showError(err, i18n.baseText('oauth.consentView.error.deny'));
	}
};

// On a failed details fetch the session is already gone (the server clears it,
// e.g. on resource_unavailable), so there's nothing to approve/deny — just leave.
const handleClose = () => {
	window.location.href = window.BASE_PATH ?? '/';
};

onMounted(async () => {
	documentTitle.set(i18n.baseText('oauth.consentView.title'));
	try {
		await consentStore.fetchConsentDetails();
	} catch (err) {
		toast.showError(err, i18n.baseText('oauth.consentView.error.fetchDetails'));
	}
});
</script>

<template>
	<div :class="$style.overlay">
		<div :class="$style['consent-dialog']">
			<header :class="$style.header">
				<div :class="[$style.logo, $style.n8n]">
					<N8nLogo size="small" :collapsed="true" release-channel="stable" />
				</div>
				<div :class="$style.arrow">
					<N8nIcon icon="arrow-right" size="large" color="text-light" />
				</div>
				<div :class="$style.logo">
					<N8nIcon icon="mcp" size="xlarge" color="text-dark" />
				</div>
			</header>
			<!-- Success screen, show while waiting to be redirected back to client -->
			<div v-if="waitingForRedirect" :class="$style.success" data-test-id="consent-success-screen">
				<N8nHeading tag="h2" size="large" :bold="true">
					{{ i18n.baseText('oauth.consentView.success.title') }}
				</N8nHeading>
				<N8nText color="text-base">
					{{ i18n.baseText('oauth.consentView.success.description') }}
				</N8nText>
			</div>
			<!-- Rejection state: the details fetch failed (e.g. the requested resource
				is no longer available), so we must not present a consent grant. -->
			<div v-else-if="error" :class="$style.content" data-test-id="consent-error">
				<N8nHeading tag="h2" size="large" :bold="true">
					{{ i18n.baseText('oauth.consentView.error.heading') }}
				</N8nHeading>
				<N8nNotice
					theme="danger"
					:data-test-id="'consent-error-notice'"
					:content="errorMessage ?? ''"
				></N8nNotice>
			</div>
			<!-- Default content -->
			<div v-else :class="$style.content" data-test-id="consent-content">
				<N8nHeading v-if="resourceName" tag="h2" size="large" :bold="true">
					{{
						i18n.baseText('oauth.consentView.headingWithWorkflow', {
							interpolate: { clientName: clientDetails?.clientName ?? '', resourceName },
						})
					}}
				</N8nHeading>
				<N8nHeading v-else tag="h2" size="large" :bold="true">
					{{
						i18n.baseText('oauth.consentView.heading', {
							interpolate: { clientName: clientDetails?.clientName ?? '' },
						})
					}}
				</N8nHeading>
				<div :class="$style['text-content']">
					<N8nText v-if="resourceName" color="text-base" size="small">
						{{
							i18n.baseText('oauth.consentView.descriptionWithWorkflow', {
								interpolate: { clientName: clientDetails?.clientName ?? '' },
							})
						}}
					</N8nText>
					<N8nText v-else color="text-base" size="small">
						{{
							i18n.baseText('oauth.consentView.description', {
								interpolate: { clientName: clientDetails?.clientName ?? '' },
							})
						}}
					</N8nText>
					<ul v-if="!resourceName" :class="$style['permission-list']">
						<li>{{ i18n.baseText('oauth.consentView.action.listWorkflows') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.workflowDetails') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.executeWorkflows') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.executionDetails') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.createUpdateWorkflows') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.createDataTables') }}</li>
						<li>{{ i18n.baseText('oauth.consentView.action.searchProjectsAndFolders') }}</li>
					</ul>
					<p :class="$style['docs-link']">
						<span
							v-n8n-html="
								i18n.baseText('oauth.consentView.readMore', {
									interpolate: {
										docsUrl: MCP_DOCS_PAGE_URL,
									},
								})
							"
						></span>
					</p>
					<N8nCallout
						v-if="clientDetails?.redirectUri"
						theme="warning"
						:class="$style['redirect-warning']"
						data-test-id="consent-redirect-warning"
					>
						<div :class="$style['redirect-warning-content']">
							<N8nText :bold="true">
								{{ i18n.baseText('oauth.consentView.redirectWarning.title') }}
							</N8nText>
							<N8nText
								:bold="true"
								:class="$style['redirect-warning-url']"
								data-test-id="consent-redirect-uri"
							>
								{{ clientDetails.redirectUri }}
							</N8nText>
							<N8nCheckbox
								v-model="redirectUriTrusted"
								:label="i18n.baseText('oauth.consentView.redirectWarning.confirm')"
							/>
						</div>
					</N8nCallout>
				</div>
			</div>
			<footer v-if="!waitingForRedirect" :class="$style.footer">
				<div :class="$style['button-group']">
					<N8nButton
						v-if="error"
						variant="solid"
						:data-test-id="'consent-close-button'"
						:size="'large'"
						@click="handleClose"
					>
						{{ i18n.baseText('generic.close') }}
					</N8nButton>
					<template v-else>
						<N8nButton
							variant="subtle"
							:data-test-id="'consent-deny-button'"
							:size="'large'"
							:loading="loading"
							:disabled="loading"
							@click="handleDeny"
						>
							{{ i18n.baseText('generic.deny') }}
						</N8nButton>
						<N8nButton
							variant="solid"
							:data-test-id="'consent-allow-button'"
							:size="'large'"
							:loading="loading"
							:disabled="allowDisabled"
							@click="handleAllow"
						>
							{{ i18n.baseText('generic.allow') }}
						</N8nButton>
					</template>
				</div>
			</footer>
		</div>
	</div>
</template>

<style module lang="scss">
.overlay {
	position: fixed;
	display: flex;
	justify-content: center;
	align-items: center;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: rgba(71, 69, 84, 0.75);
	z-index: 1000;
}

.consent-dialog {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	min-width: 500px;
	max-width: 70%;
	padding: var(--spacing--lg);
	background-color: var(--color--background--light-3);
	border: var(--border);
	border-radius: var(--radius--lg);
}

.header {
	display: flex;
	align-items: center;
	gap: var(--spacing--xs);
}

.logo {
	display: flex;
	justify-content: center;
	align-items: center;
	width: var(--spacing--2xl);
	height: var(--spacing--2xl);
	border: var(--border);
	border-radius: var(--radius);

	&.n8n > div {
		position: relative;
		bottom: var(--spacing--5xs);
	}
}

.content {
	padding: var(--spacing--lg);
	display: flex;
	flex-direction: column;
	gap: var(--spacing--sm);
}

.success {
	padding: var(--spacing--xl);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--spacing--2xs);
	text-align: center;
}

.text-content {
	display: flex;
	flex-direction: column;
	gap: var(--spacing--sm);
}

.permission-list {
	display: flex;
	flex-direction: column;
	gap: var(--spacing--2xs);
	padding-left: var(--spacing--lg);
	list-style-type: disc;

	li {
		color: var(--color--text);
		font-size: var(--font-size--2xs);
	}
}

.docs-link {
	color: var(--color--text);
	font-size: var(--font-size--2xs);
}

.redirect-warning {
	margin-top: var(--spacing--2xs);
}

.redirect-warning-content {
	display: flex;
	flex-direction: column;
	gap: var(--spacing--3xs);
}

.redirect-warning-url {
	word-break: break-all;
}

.footer {
	width: 100%;
	display: flex;
	flex-direction: column;
	justify-content: center;
	gap: var(--spacing--sm);

	:global(.notice) {
		margin: 0;
	}

	.button-group {
		display: flex;
		justify-content: center;
		gap: var(--spacing--2xs);
	}
}
</style>
