<script setup lang="ts">
import type { SimplifiedNodeType } from '@/Interface';
import {
	CREDENTIAL_ONLY_NODE_PREFIX,
	DEFAULT_SUBCATEGORY,
	DRAG_EVENT_DATA_KEY,
	HITL_SUBCATEGORY,
	HUMAN_IN_THE_LOOP_CATEGORY,
} from '@/app/constants';
import { COMMUNITY_NODES_INSTALLATION_DOCS_URL } from '@/features/settings/communityNodes/communityNodes.constants';
import { computed, ref } from 'vue';

import NodeIcon from '@/app/components/NodeIcon.vue';
import { getNodeIconSize } from '@/app/utils/nodeIcon';
import { useNodeCreatorStore } from '@/features/shared/nodeCreator/nodeCreator.store';
import { isCommunityPackageName } from 'n8n-workflow';
import OfficialIcon from 'virtual:icons/mdi/verified';

import { useNodeType } from '@/app/composables/useNodeType';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { useI18n } from '@n8n/i18n';
import { useActions } from '../../composables/useActions';
import { useViewStacks } from '../../composables/useViewStacks';
import {
	isNodePreviewKey,
	removePreviewToken,
	shouldShowCommunityNodeDetails,
} from '../../nodeCreator.utils';

import { N8nIcon, N8nNodeCreatorNode, N8nTooltip } from '@n8n/design-system';
export interface Props {
	nodeType: SimplifiedNodeType;
	subcategory?: string;
	active?: boolean;
}

const props = withDefaults(defineProps<Props>(), {
	active: false,
	subcategory: undefined,
});

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

const { actions } = useNodeCreatorStore();
const { getAddedNodesAndConnections } = useActions();
const { activeViewStack } = useViewStacks();
const { isSubNodeType } = useNodeType({
	nodeType: props.nodeType,
});
const nodeTypesStore = useNodeTypesStore();

const dragging = ref(false);
const draggablePosition = ref({ x: -100, y: -100 });
const draggableDataTransfer = ref(null as Element | null);

const description = computed<string>(() => {
	if (isCommunityNodePreview.value) {
		return props.nodeType.description;
	}
	if (isSendAndWaitCategory.value) {
		return '';
	}
	if (
		props.subcategory === DEFAULT_SUBCATEGORY &&
		!props.nodeType.name.startsWith(CREDENTIAL_ONLY_NODE_PREFIX)
	) {
		return '';
	}

	return i18n.headerText({
		key: `headers.${shortNodeType.value}.description`,
		fallback: props.nodeType.description,
	});
});

const showActionArrow = computed(() => {
	if (shouldShowCommunityNodeDetails(isCommunityNode.value, activeViewStack)) {
		return true;
	}

	return hasActions.value && !isSendAndWaitCategory.value;
});
const isSendAndWaitCategory = computed(
	() =>
		activeViewStack.subcategory === HITL_SUBCATEGORY ||
		activeViewStack.rootView === HUMAN_IN_THE_LOOP_CATEGORY,
);
const dataTestId = computed(() =>
	hasActions.value ? 'node-creator-action-item' : 'node-creator-node-item',
);

const hasActions = computed(() => {
	return nodeActions.value.length > 1 && !activeViewStack.hideActions;
});

const nodeActions = computed(() => {
	return actions[props.nodeType.name] || [];
});

const nodeListIconSize = computed(() => {
	const icon = props.nodeType.icon;
	const iconName = typeof icon === 'string' ? icon : undefined;
	return getNodeIconSize('nodeList', iconName);
});

const shortNodeType = computed<string>(() => i18n.shortNodeType(props.nodeType.name) || '');

const draggableStyle = computed<{ top: string; left: string }>(() => ({
	top: `${draggablePosition.value.y}px`,
	left: `${draggablePosition.value.x}px`,
}));

const isCommunityNode = computed<boolean>(() => isCommunityPackageName(props.nodeType.name));
const isCommunityNodePreview = computed<boolean>(() => isNodePreviewKey(props.nodeType.name));

const displayName = computed<string>(() => {
	const trimmedDisplayName = props.nodeType.displayName.trimEnd();

	return i18n.headerText({
		key: `headers.${shortNodeType.value}.displayName`,
		fallback: hasActions.value ? trimmedDisplayName.replace('Trigger', '') : trimmedDisplayName,
	});
});

const isTrigger = computed<boolean>(() => {
	return props.nodeType.group.includes('trigger') && !hasActions.value;
});

const communityNodeType = computed(() => {
	return nodeTypesStore.communityNodeType(removePreviewToken(props.nodeType.name));
});

const isOfficial = computed(() => {
	return communityNodeType.value?.isOfficialNode ?? false;
});

const author = computed(() => {
	return communityNodeType.value?.displayName ?? displayName.value;
});

const tag = computed(() => {
	if (props.nodeType.tag) {
		return props.nodeType.tag;
	}
	if (description.value.toLowerCase().includes('deprecated')) {
		return { text: i18n.baseText('nodeCreator.nodeItem.deprecated'), type: 'info' };
	}
	return undefined;
});

// Only surface the "new" badge in search results — under the category itself
// the parent subcategory tile already carries the badge.
const showNewBadge = computed(() => Boolean(props.nodeType.isNew && activeViewStack.search));

function onDragStart(event: DragEvent): void {
	if (event.dataTransfer) {
		event.dataTransfer.effectAllowed = 'copy';
		event.dataTransfer.dropEffect = 'copy';
		event.dataTransfer.setDragImage(draggableDataTransfer.value as Element, 0, 0);
		event.dataTransfer.setData(
			DRAG_EVENT_DATA_KEY,
			JSON.stringify(getAddedNodesAndConnections([{ type: props.nodeType.name }])),
		);
	}

	dragging.value = true;
}

function onDragEnd(): void {
	dragging.value = false;
	setTimeout(() => {
		draggablePosition.value = { x: -100, y: -100 };
	}, 300);
}

function onCommunityNodeTooltipClick(event: MouseEvent) {
	if ((event.target as Element).localName === 'a') {
		telemetry.track('user clicked cnr docs link', { source: 'nodes panel node' });
	}
}
</script>

<template>
	<!-- Node Item is draggable only if it doesn't contain actions -->
	<N8nNodeCreatorNode
		:draggable="!showActionArrow"
		:class="$style.nodeItem"
		:description="description"
		:title="displayName"
		:show-action-arrow="showActionArrow"
		:is-trigger="isTrigger"
		:is-official="isOfficial"
		:data-test-id="dataTestId"
		:tag="tag"
		:is-new="showNewBadge"
		@dragstart="onDragStart"
		@dragend="onDragEnd"
	>
		<template #icon>
			<div :class="$style.iconWrapper">
				<div v-if="isSubNodeType" :class="$style.subNodeBackground"></div>
				<NodeIcon
					:class="$style.nodeIcon"
					:node-type="nodeType"
					:size="nodeListIconSize"
					color-default="var(--color--foreground--shade-2)"
				/>
			</div>
		</template>

		<template v-if="isOfficial" #extraDetails>
			<N8nTooltip placement="top" :show-after="500">
				<template #content>
					{{ i18n.baseText('generic.officialNode.tooltip', { interpolate: { author: author } }) }}
				</template>
				<OfficialIcon :class="[$style.icon, $style.official]" />
			</N8nTooltip>
		</template>

		<template
			v-else-if="
				isCommunityNode && !isCommunityNodePreview && !activeViewStack?.communityNodeDetails
			"
			#extraDetails
		>
			<N8nTooltip placement="top" :show-after="500">
				<template #content>
					<p
						v-n8n-html="
							i18n.baseText('generic.communityNode.tooltip', {
								interpolate: {
									packageName: nodeType.name.split('.')[0],
									docURL: COMMUNITY_NODES_INSTALLATION_DOCS_URL,
								},
							})
						"
						:class="$style.communityNodeIcon"
						@click="onCommunityNodeTooltipClick"
					/>
				</template>
				<N8nIcon size="small" :class="$style.icon" icon="box" />
			</N8nTooltip>
		</template>
		<template #dragContent>
			<div
				v-show="dragging"
				ref="draggableDataTransfer"
				:class="$style.draggable"
				:style="draggableStyle"
			>
				<NodeIcon
					:node-type="nodeType"
					:size="40"
					:shrink="false"
					color-default="var(--color--foreground--shade-2)"
					@click.capture.stop
				/>
			</div>
		</template>
	</N8nNodeCreatorNode>
</template>

<style lang="scss" module>
.nodeItem {
	--trigger-icon--color--background: #{$trigger-icon-background-color};
	--trigger-icon--border-color: #{$trigger-icon-border-color};
	margin-left: 15px;
	margin-right: 12px;
	user-select: none;
}

.iconWrapper {
	position: relative;
	display: flex;
	align-items: center;
	justify-content: center;
}

.nodeIcon {
	z-index: 2;
}

.subNodeBackground {
	background-color: var(--node-type--supplemental--color--background);
	border-radius: 50%;
	height: 40px;
	width: 40px;
	position: absolute;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	z-index: 1;
}

.communityNodeIcon {
	vertical-align: top;
}

.draggable {
	width: 100px;
	height: 100px;
	position: fixed;
	z-index: 1;
	opacity: 0.66;
	border: 2px solid var(--color--foreground--shade-2);
	border-radius: var(--radius--lg);
	background-color: var(--color--background--light-3);
	display: flex;
	justify-content: center;
	align-items: center;
}

.draggableDataTransfer {
	width: 1px;
	height: 1px;
}

.icon {
	display: inline-flex;
	color: var(--color--text);
	width: 12px;

	&.official {
		width: 14px;
	}
}
</style>
