<script setup lang="ts">
import Modal from '@/app/components/Modal.vue';
import {
	COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY,
	COMMUNITY_PACKAGE_MANAGE_ACTIONS,
} from '../communityNodes.constants';
import { useToast } from '@/app/composables/useToast';
import { useCommunityNodesStore } from '../communityNodes.store';
import { createEventBus } from '@n8n/utils/event-bus';
import { useI18n } from '@n8n/i18n';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { computed, onMounted, ref } from 'vue';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import type { CommunityNodeType } from '@n8n/api-types';
import { useSettingsStore } from '@/app/stores/settings.store';
import semver from 'semver';
import { useUIStore } from '@/app/stores/ui.store';
import { useWorkflowsListStore } from '@/app/stores/workflowsList.store';
import type { WorkflowResource } from '@/Interface';

import { N8nButton, N8nNotice, N8nText } from '@n8n/design-system';
import NodesInWorkflowTable from './NodesInWorkflowTable.vue';

export type CommunityPackageManageMode = 'uninstall' | 'update' | 'view-documentation';

interface Props {
	modalName: string;
	activePackageName: string;
	mode: CommunityPackageManageMode;
}

const props = defineProps<Props>();

const communityNodesStore = useCommunityNodesStore();
const nodeTypesStore = useNodeTypesStore();
const settingsStore = useSettingsStore();
const workflowsListStore = useWorkflowsListStore();

const modalBus = createEventBus();

const toast = useToast();
const i18n = useI18n();
const telemetry = useTelemetry();

const loading = ref(false);

const workflowsWithPackageNodes = ref<WorkflowResource[]>([]);

const isUsingVerifiedAndUnverifiedPackages =
	settingsStore.isCommunityNodesFeatureEnabled && settingsStore.isUnverifiedPackagesEnabled;
const isUsingVerifiedPackagesOnly =
	settingsStore.isCommunityNodesFeatureEnabled && !settingsStore.isUnverifiedPackagesEnabled;

const communityStorePackage = computed(
	() => communityNodesStore.installedPackages[props.activePackageName],
);
const updateVersion = computed(() => {
	return settingsStore.isUnverifiedPackagesEnabled
		? communityStorePackage.value.updateAvailable
		: nodeTypeStorePackage.value?.npmVersion;
});
const nodeTypeStorePackage = ref<CommunityNodeType>();

const isLatestPackageVerified = ref<boolean>(true);

const packageVersion = ref<string>(communityStorePackage.value.updateAvailable ?? '');

const includedNodes = computed(() => {
	return communityStorePackage.value.installedNodes.map((node) => node.name).join(', ');
});

const getModalContent = computed(() => {
	if (props.mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UNINSTALL) {
		return {
			title: i18n.baseText('settings.communityNodes.confirmModal.uninstall.title'),
			message: i18n.baseText('settings.communityNodes.confirmModal.includedNodes', {
				interpolate: {
					nodes: includedNodes.value,
				},
			}),
			description: workflowsWithPackageNodes.value.length
				? i18n.baseText('settings.communityNodes.confirmModal.uninstall.description')
				: i18n.baseText('settings.communityNodes.confirmModal.noWorkflowsUsingNodes'),
			buttonLabel: i18n.baseText('settings.communityNodes.confirmModal.uninstall.buttonLabel'),
			buttonLoadingLabel: i18n.baseText(
				'settings.communityNodes.confirmModal.uninstall.buttonLoadingLabel',
			),
		};
	}
	return {
		title: i18n.baseText('settings.communityNodes.confirmModal.update.title'),
		message: i18n.baseText('settings.communityNodes.confirmModal.includedNodes', {
			interpolate: {
				nodes: includedNodes.value,
			},
		}),
		description: workflowsWithPackageNodes.value.length
			? i18n.baseText('settings.communityNodes.confirmModal.update.description')
			: i18n.baseText('settings.communityNodes.confirmModal.noWorkflowsUsingNodes'),
		warning: i18n.baseText('settings.communityNodes.confirmModal.update.warning'),
		buttonLabel: i18n.baseText('settings.communityNodes.confirmModal.update.buttonLabel'),
		buttonLoadingLabel: i18n.baseText(
			'settings.communityNodes.confirmModal.update.buttonLoadingLabel',
		),
	};
});

const onModalClose = () => {
	return !loading.value;
};

const onConfirmButtonClick = async () => {
	if (props.mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UNINSTALL) {
		await onUninstall();
	} else if (props.mode === COMMUNITY_PACKAGE_MANAGE_ACTIONS.UPDATE) {
		await onUpdate();
	}
};

const onUninstall = async () => {
	try {
		telemetry.track('user started cnr package deletion', {
			package_name: communityStorePackage.value.packageName,
			package_node_names: communityStorePackage.value.installedNodes.map((node) => node.name),
			package_version: communityStorePackage.value.installedVersion,
			package_author: communityStorePackage.value.authorName,
			package_author_email: communityStorePackage.value.authorEmail,
		});
		loading.value = true;
		await communityNodesStore.uninstallPackage(props.activePackageName);
		await Promise.all([
			useNodeTypesStore().getNodeTypes(),
			useNodeTypesStore().fetchCommunityNodePreviews(),
		]);
		toast.showMessage({
			title: i18n.baseText('settings.communityNodes.messages.uninstall.success.title'),
			type: 'success',
		});
	} catch (error) {
		toast.showError(error, i18n.baseText('settings.communityNodes.messages.uninstall.error'));
	} finally {
		loading.value = false;
		modalBus.emit('close');
	}
};

const onUpdate = async () => {
	try {
		telemetry.track('user started cnr package update', {
			package_name: communityStorePackage.value.packageName,
			package_node_names: communityStorePackage.value.installedNodes.map((node) => node.name),
			package_version_current: communityStorePackage.value.installedVersion,
			package_version_new: communityStorePackage.value.updateAvailable,
			package_author: communityStorePackage.value.authorName,
			package_author_email: communityStorePackage.value.authorEmail,
		});
		loading.value = true;

		if (settingsStore.isUnverifiedPackagesEnabled) {
			await communityNodesStore.updatePackage(props.activePackageName);
		} else if (settingsStore.isCommunityNodesFeatureEnabled) {
			await communityNodesStore.updatePackage(
				props.activePackageName,
				updateVersion.value,
				nodeTypeStorePackage.value?.checksum,
			);
		} else {
			throw new Error('Community nodes feature is not correctly enabled.');
		}

		await useNodeTypesStore().getNodeTypes();
		toast.showMessage({
			title: i18n.baseText('settings.communityNodes.messages.update.success.title'),
			message: i18n.baseText('settings.communityNodes.messages.update.success.message', {
				interpolate: {
					packageName: props.activePackageName,
					version: updateVersion.value ?? '',
				},
			}),
			type: 'success',
		});
	} catch (error) {
		toast.showError(error, i18n.baseText('settings.communityNodes.messages.update.error.title'));
	} finally {
		loading.value = false;
		modalBus.emit('close');
	}
};

async function fetchPackageInfo(packageName: string) {
	await nodeTypesStore.loadNodeTypesIfNotLoaded();
	const nodeType = nodeTypesStore.visibleNodeTypes.find((nodeType) =>
		nodeType.name.includes(packageName),
	);

	if (nodeType) {
		const communityNodeAttributes = await nodeTypesStore.getCommunityNodeAttributes(nodeType?.name);

		nodeTypeStorePackage.value = communityNodeAttributes ?? undefined;
	}
}

function setIsVerifiedLatestPackage() {
	if (
		isUsingVerifiedAndUnverifiedPackages &&
		nodeTypeStorePackage.value?.npmVersion &&
		communityStorePackage.value.updateAvailable
	) {
		isLatestPackageVerified.value = semver.eq(
			nodeTypeStorePackage.value.npmVersion,
			communityStorePackage.value.updateAvailable,
		);
	}
}

function setPackageVersion() {
	if (isUsingVerifiedPackagesOnly) {
		packageVersion.value = nodeTypeStorePackage.value?.npmVersion ?? packageVersion.value;
	}
}

const onClick = async () => {
	useUIStore().closeModal(COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY);
};

onMounted(async () => {
	if (props.activePackageName) {
		await fetchPackageInfo(props.activePackageName);
	}

	if (communityStorePackage.value?.installedNodes.length) {
		const nodeTypes = communityStorePackage.value.installedNodes.map((node) => node.type);
		const response = await workflowsListStore.fetchWorkflowsWithNodesIncluded(nodeTypes);
		workflowsWithPackageNodes.value = response?.data ?? [];
	}

	setIsVerifiedLatestPackage();
	setPackageVersion();
});
</script>

<template>
	<Modal
		width="640px"
		:name="COMMUNITY_PACKAGE_CONFIRM_MODAL_KEY"
		:title="getModalContent.title"
		:event-bus="modalBus"
		:center="true"
		:show-close="!loading"
		:before-close="onModalClose"
	>
		<template #content>
			<N8nText color="text-dark" :bold="true">{{ getModalContent.message }}</N8nText>
			<N8nNotice
				v-if="!isLatestPackageVerified && getModalContent.warning"
				data-test-id="communityPackageManageConfirmModal-warning"
				:content="getModalContent.warning"
			/>
			<div :class="$style.descriptionContainer">
				<N8nText size="medium" color="text-base">
					{{ getModalContent.description }}
				</N8nText>
			</div>

			<NodesInWorkflowTable
				v-if="workflowsWithPackageNodes?.length"
				:data="workflowsWithPackageNodes"
			/>
		</template>
		<template #footer>
			<div :class="$style.footerContainer">
				<N8nButton
					variant="subtle"
					:label="i18n.baseText('settings.communityNodes.confirmModal.cancel')"
					size="large"
					data-test-id="close-button"
					@click="onClick"
				/>
				<N8nButton
					:loading="loading"
					:disabled="loading"
					:label="loading ? getModalContent.buttonLoadingLabel : getModalContent.buttonLabel"
					size="large"
					@click="onConfirmButtonClick"
				/>
			</div>
		</template>
	</Modal>
</template>

<style module lang="scss">
.descriptionContainer {
	display: flex;
	margin: var(--spacing--sm) 0;
	flex-direction: column;
}

.footerContainer {
	display: flex;
	align-items: center;
	justify-content: space-between;
}

.descriptionIcon {
	align-self: center;
	color: var(--color--text--tint-2);
}

.descriptionText {
	padding: 0 var(--spacing--xs);
}
</style>
