<script lang="ts" setup>
import { useI18n } from '@n8n/i18n';
import { injectNDVStore } from '@/features/ndv/shared/ndv.store';
import { useNodeTypesStore } from '@/app/stores/nodeTypes.store';
import { injectWorkflowDocumentStore } from '@/app/stores/workflowDocument.store';
import { isPresent } from '@/app/utils/typesUtils';
import type { IConnectedNode } from 'n8n-workflow';
import { computed } from 'vue';
import NodeIcon from '@/app/components/NodeIcon.vue';
import { truncate } from '@n8n/utils/string/truncate';

import { N8nOption, N8nSelect } from '@n8n/design-system';
type Props = {
	nodes: IConnectedNode[];
	modelValue: string | null;
};

const props = defineProps<Props>();

const emit = defineEmits<{
	'update:model-value': [value: string];
}>();

const i18n = useI18n();
const workflowDocumentStore = injectWorkflowDocumentStore();
const nodeTypesStore = useNodeTypesStore();
const ndvStore = injectNDVStore();

const selectedInputNode = computed(
	() => workflowDocumentStore?.value?.getNodeByName(props.modelValue ?? '') ?? null,
);

const selectedInputNodeType = computed(() => {
	const node = selectedInputNode.value;
	if (!node) return null;

	return nodeTypesStore.getNodeType(node.type, node.typeVersion);
});

const inputNodes = computed(
	() =>
		props.nodes
			?.map((node) => {
				const fullNode = workflowDocumentStore?.value?.getNodeByName(node.name) ?? null;
				if (!fullNode) return null;

				return {
					node: fullNode,
					type: nodeTypesStore.getNodeType(fullNode.type, fullNode.typeVersion),
					depth: node.depth,
				};
			})
			.filter(isPresent) ?? [],
);

const activeNode = computed(() => ndvStore.value.activeNode);

const activeNodeType = computed(() => {
	const node = activeNode.value;
	if (!node) return null;

	return nodeTypesStore.getNodeType(node.type, node.typeVersion);
});

const isMultiInputNode = computed(() => {
	const nodeType = activeNodeType.value;
	return nodeType !== null && nodeType.inputs.length > 1;
});

const connectedTo = (nodeName: string) => {
	const connections = ndvStore.value.ndvNodeInputNumber[nodeName];
	if (!connections) return '';
	if (connections.length === 1) {
		return `Input ${ndvStore.value.ndvNodeInputNumber[nodeName]}`;
	}
	return `Inputs ${ndvStore.value.ndvNodeInputNumber[nodeName].join(', ')}`;
};

function getMultipleNodesText(nodeName: string): string {
	if (
		!nodeName ||
		!isMultiInputNode.value ||
		!activeNode.value ||
		!activeNodeType.value?.inputNames
	)
		return '';

	const activeNodeConnections =
		workflowDocumentStore?.value?.connectionsByDestinationNode[activeNode.value.name]?.main ?? [];
	// Collect indexes of connected nodes
	const connectedInputIndexes = activeNodeConnections.reduce((acc: number[], node, index) => {
		if (node?.[0] && node[0].node === nodeName) return [...acc, index];
		return acc;
	}, []);

	// Match connected input indexes to their names specified by active node
	const connectedInputs = connectedInputIndexes.map(
		(inputIndex) => activeNodeType.value?.inputNames?.[inputIndex],
	);

	if (connectedInputs.length === 0) return '';

	return `(${connectedInputs.join(' & ')})`;
}

function title(nodeName: string, length = 30) {
	return truncate(nodeName, length);
}

function subtitle(nodeName: string, depth: number) {
	const multipleNodesText = getMultipleNodesText(nodeName);
	if (multipleNodesText) return multipleNodesText;
	return i18n.baseText('ndv.input.nodeDistance', { adjustToNumber: depth });
}

function onInputNodeChange(value: string) {
	emit('update:model-value', value);
}
</script>

<template>
	<N8nSelect
		:model-value="modelValue"
		:no-data-text="i18n.baseText('ndv.input.noNodesFound')"
		:placeholder="i18n.baseText('ndv.input.parentNodes')"
		:class="$style.select"
		teleported
		size="small"
		filterable
		data-test-id="ndv-input-select"
		@update:model-value="onInputNodeChange"
	>
		<template #prefix>
			<NodeIcon
				:disabled="selectedInputNode?.disabled"
				:node-type="selectedInputNodeType"
				:size="14"
				:shrink="false"
			/>
		</template>

		<N8nOption
			v-for="{ node, type, depth } of inputNodes"
			:key="node.name"
			:value="node.name"
			:class="[$style.node, { [$style.disabled]: node.disabled }]"
			:label="`${title(node.name)} ${getMultipleNodesText(node.name)}`"
			data-test-id="ndv-input-option"
		>
			<NodeIcon
				:disabled="node.disabled"
				:node-type="type"
				:size="14"
				:shrink="false"
				:class="$style.icon"
			/>
			<span :class="$style.title">
				{{ title(node.name) }}
				<span v-if="node.disabled">({{ i18n.baseText('node.disabled') }})</span>
			</span>

			<span :class="$style.subtitle">{{
				connectedTo(node.name) ? connectedTo(node.name) : subtitle(node.name, depth)
			}}</span>
		</N8nOption>
	</N8nSelect>
</template>

<style lang="scss" module>
.select {
	--node-input--select--max-width: 224px;
	max-width: var(--node-input--select--max-width);

	:global(.el-input--suffix .el-input__inner) {
		padding-left: calc(var(--spacing--lg) + var(--spacing--4xs));
		padding-right: var(--spacing--lg);
	}
}

.node {
	--select--option--padding: 0 var(--spacing--sm);
	display: flex;
	align-items: center;
	font-size: var(--font-size--2xs);
	gap: var(--spacing--4xs);
}

.icon {
	padding-right: var(--spacing--4xs);
}

.title {
	color: var(--color--text--shade-1);
	font-weight: var(--font-weight--regular);
	max-width: var(--node-input--select--max-width);
	overflow: hidden;
	white-space: nowrap;
	text-overflow: ellipsis;
}

.disabled .title {
	color: var(--color--text--tint-1);
}

.subtitle {
	margin-left: auto;
	padding-left: var(--spacing--2xs);
	color: var(--color--text--tint-1);
	font-weight: var(--font-weight--regular);
}
</style>
