<script lang="ts" setup>
import TextWithHighlights from './TextWithHighlights.vue';
import { type IconName } from '@n8n/design-system/components/N8nIcon/icons';
import { saveAs } from 'file-saver';
import { useUIStore } from '@/app/stores/ui.store';
import { useWorkflowsStore } from '@/app/stores/workflows.store';
import { BINARY_DATA_VIEW_MODAL_KEY } from '@/app/constants';
import type { BinaryMetadata } from '@n8n/design-system';
import { useToast } from '@/app/composables/useToast';
import { useI18n } from '@n8n/i18n';

import { N8nIcon, N8nTooltip } from '@n8n/design-system';
type Props = {
	title?: string;
	path?: string;
	level?: number;
	depth?: number;
	expression?: string;
	value?: string;
	id: string;
	icon: IconName;
	collapsable?: boolean;
	nodeName?: string;
	nodeType?: string;
	highlight?: boolean;
	draggable?: boolean;
	collapsed?: boolean;
	search?: string;
	preview?: boolean;
	locked?: boolean;
	lockedTooltip?: string;
	runIndex?: number;
	binaryData?: BinaryMetadata;
};

const props = defineProps<Props>();

const i18n = useI18n();

async function downloadBinaryData() {
	if (!props.binaryData) return;
	try {
		const { id, fileName, mimeType, fileExtension } = props.binaryData;
		const url = useWorkflowsStore().getBinaryUrl(id, 'download', fileName ?? '', mimeType);
		let name = fileName ?? 'file';
		if (!name.includes('.') && fileExtension) {
			name = [name, fileExtension].join('.');
		}
		const response = await fetch(url);
		if (!response.ok) throw new Error('Error downloading file');
		const blob = await response.blob();
		saveAs(blob, name);
	} catch (error) {
		useToast().showMessage({
			title: i18n.baseText('runData.downloadBinaryData.error.title'),
			message: i18n.baseText('runData.downloadBinaryData.error.message'),
			type: 'error',
		});
	}
}

function viewBinaryData() {
	useUIStore().openModalWithData({
		name: BINARY_DATA_VIEW_MODAL_KEY,
		data: {
			binaryData: props.binaryData,
		},
	});
}

const emit = defineEmits<{
	click: [];
}>();
</script>

<template>
	<div class="schema-item" :class="{ draggable }" data-test-id="run-data-schema-item">
		<div class="toggle-container">
			<div v-if="collapsable" class="toggle" @click="emit('click')">
				<N8nIcon icon="chevron-down" :class="{ 'collapse-icon': true, collapsed }" />
			</div>
		</div>
		<div
			v-if="title"
			:data-name="title"
			:data-path="path"
			:data-depth="depth"
			:data-nest-level="level"
			:data-value="expression"
			:data-node-type="nodeType"
			:data-target="!locked && 'mappable'"
			:data-node-name="nodeName"
			:data-run-index="runIndex"
			class="pill"
			:class="{
				'pill--highlight': highlight,
				'pill--preview': preview,
				'pill--locked': locked,
			}"
			data-test-id="run-data-schema-node-name"
		>
			<N8nIcon class="type-icon" :icon="icon" size="small" />
			<TextWithHighlights class="title" :content="title" :search="props.search" />
		</div>

		<N8nTooltip v-if="locked" :disabled="!lockedTooltip" :popper-class="$style.tooltip">
			<template #content>
				<span v-n8n-html="lockedTooltip" />
			</template>
			<N8nIcon class="locked-icon" icon="lock" size="small" />
		</N8nTooltip>

		<TextWithHighlights
			data-test-id="run-data-schema-item-value"
			class="text"
			:content="value"
			:search="props.search"
		/>
		<div v-if="props.binaryData && !preview" class="binary-controls">
			<div
				class="pill binary-control-pill"
				:class="{
					'pill--highlight': highlight,
					'pill--preview': preview,
					'pill--locked': locked,
				}"
				@click="downloadBinaryData"
			>
				<N8nIcon class="type-icon" :icon="'download'" size="small" />
			</div>
			<div
				class="pill binary-control-pill"
				:class="{
					'pill--highlight': highlight,
					'pill--preview': preview,
					'pill--locked': locked,
				}"
				@click="viewBinaryData"
			>
				<N8nIcon class="type-icon" :icon="'eye'" size="small" />
			</div>
		</div>
	</div>
</template>

<style lang="css" scoped>
.schema-item {
	display: flex;
	margin-left: calc(var(--spacing--lg) * v-bind(level));
	align-items: baseline;
	padding-bottom: var(--spacing--2xs);
}

.toggle-container {
	min-width: var(--spacing--lg);
	min-height: 17px;
}

.toggle {
	cursor: pointer;
	display: flex;
	justify-content: center;
	cursor: pointer;
	font-size: var(--font-size--sm);
	color: var(--color--text--tint-1);
}

.pill {
	display: inline-flex;
	height: 24px;
	padding: 0 var(--spacing--3xs);
	border: 1px solid var(--color--foreground--tint-1);
	border-radius: var(--radius);
	background-color: var(--color--background--light-3);
	font-size: var(--font-size--2xs);
	color: var(--color--text--shade-1);
	max-width: 50%;
	align-items: center;

	> *:not(:first-child) {
		margin-left: var(--spacing--3xs);
		padding-left: var(--spacing--3xs);
		border-left: 1px solid var(--color--foreground--tint-1);
	}

	&.pill--preview {
		/* Cannot use CSS variable inside data URL, so instead switching based on data-theme and media query */
		--schema-preview--border--dashed: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' viewBox='0 0 400 400' fill='none' rx='4' ry='4' stroke='%230000002A' stroke-width='2' stroke-dasharray='4%2c 4' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
		--schema-preview--border--dashed--dark: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' viewBox='0 0 400 400' fill='none' rx='4' ry='4' stroke='%23FFFFFF2A' stroke-width='2' stroke-dasharray='4%2c 4' stroke-dashoffset='0' stroke-linecap='square'/%3e%3c/svg%3e");
		color: var(--color--text--tint-1);
		background-color: var(--color--foreground--tint-1);
		border: none;
		max-width: calc(100% - var(--spacing--lg));
		background-image: var(--schema-preview--border--dashed);

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

@media (prefers-color-scheme: dark) {
	body:not([data-theme]) .pill--preview {
		background-image: var(--schema-preview--border--dashed--dark);
	}
}

[data-theme='dark'] .pill--preview {
	background-image: var(--schema-preview--border--dashed--dark);
}

.draggable .pill.pill--highlight {
	color: var(--color--primary);
	border-color: var(--color--primary--tint-1);
	background-color: var(--color--primary--tint-3);
}

.draggable .pill.pill--highlight .type-icon {
	color: var(--color--primary);
}

.draggable .pill:not(.pill--locked) {
	cursor: grab;
}

.draggable .pill:not(.pill--locked):hover {
	background-color: var(--color--background--light-2);
	border-color: var(--color--foreground);
}

.type-icon {
	color: var(--color--text--tint-1);
}

.locked-icon {
	color: var(--color--text--tint-1);
	margin-left: var(--spacing--2xs);
}

.title {
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	line-height: 1.5;
}

.text {
	font-weight: var(--font-weight--regular);
	font-size: var(--font-size--2xs);
	margin-left: var(--spacing--2xs);
	word-break: break-word;
}

.collapse-icon {
	transition: transform 0.2s cubic-bezier(0.19, 1, 0.22, 1);
}
.collapsed {
	transform: rotateZ(-90deg);
}

.tooltip {
	max-width: 260px;
}

.binary-controls {
	display: inline-flex;
	gap: var(--spacing--3xs);
	opacity: 0;
	transition: all 0.2s ease-in-out;
	cursor: pointer;
}

.schema-item:hover .binary-controls {
	opacity: 1;
}

.binary-control-pill:hover .type-icon {
	color: var(--color--primary);
}
</style>

<style lang="css" module>
.tooltip {
	max-width: 260px;
}
</style>
