<script setup lang="ts">
import { useFocusPanelStore } from '@/app/stores/focusPanel.store';
import { injectWorkflowDocumentStore } from '@/app/stores/workflowDocument.store';
import { useExperimentalNdvStore } from '@/features/workflows/canvas/experimental/experimentalNdv.store';
import { useSetupPanelStore } from '@/features/setupPanel/setupPanel.store';
import { useInjectWorkflowId } from '@/app/composables/useInjectWorkflowId';
import { useTelemetry } from '@/app/composables/useTelemetry';
import { useDeviceSupport } from '@n8n/composables/useDeviceSupport';
import { useTelemetryContext } from '@/app/composables/useTelemetryContext';
import { computed, onMounted, watch, useTemplateRef, onBeforeUnmount } from 'vue';
import { storeToRefs } from 'pinia';
import { useVueFlow } from '@vue-flow/core';
import { useActiveElement, useThrottleFn } from '@vueuse/core';
import { type CanvasNode, CanvasNodeRenderType } from '@/features/workflows/canvas/canvas.types';
import { type ContextMenuAction } from '@/features/shared/contextMenu/composables/useContextMenuItems';
import type { INodeUi, ResizeData } from '@/Interface';
import { N8nResizeWrapper } from '@n8n/design-system';
import FocusSidebarTabs from '@/features/setupPanel/components/FocusSidebarTabs.vue';
import SetupPanel from '@/features/setupPanel/components/SetupPanel.vue';
import FocusPanel from '@/app/components/FocusPanel.vue';
import EvaluationsWizardSidepanel from '@/features/ai/evaluation.ee/components/WizardSidepanel/EvaluationsWizardSidepanel.vue';
import EvaluationsPaywall from '@/features/ai/evaluation.ee/components/Paywall/EvaluationsPaywall.vue';
import { useEvaluationsWizardSidepanelExperiment } from '@/experiments/evaluationsWizardSidepanel/useEvaluationsWizardSidepanelExperiment';
import { useAiRootNodes } from '@/features/ai/evaluation.ee/composables/useAiRootNodes';
import { useEvaluationsLicense } from '@/features/ai/evaluation.ee/composables/useEvaluationsLicense';

defineOptions({ name: 'FocusSidebar' });

defineProps<{
	isCanvasReadOnly: boolean;
}>();

const emit = defineEmits<{
	contextMenuAction: [action: ContextMenuAction, nodeIds: string[]];
}>();

const wrapperRef = useTemplateRef('wrapper');

const workflowId = useInjectWorkflowId();
const focusPanelStore = useFocusPanelStore();
const workflowDocumentStore = injectWorkflowDocumentStore();
const experimentalNdvStore = useExperimentalNdvStore();
const setupPanelStore = useSetupPanelStore();
const telemetry = useTelemetry();
const deviceSupport = useDeviceSupport();
const vueFlow = useVueFlow(workflowId.value);
const activeElement = useActiveElement();

useTelemetryContext({ view_shown: 'focus_panel' });

const { selectedTab } = storeToRefs(focusPanelStore);
const focusPanelActive = computed(() => focusPanelStore.focusPanelActive);
const focusPanelWidth = computed(() => focusPanelStore.focusPanelWidth);
const resolvedParameter = computed(() => focusPanelStore.resolvedParameter);

const isSetupPanelEnabled = computed(() => setupPanelStore.isFeatureEnabled);
const { isFeatureEnabled: isEvaluationsWizardSidepanelEnabled } =
	useEvaluationsWizardSidepanelExperiment();
const aiRootNodes = useAiRootNodes();
const hasAiRootNode = computed(() => aiRootNodes.value.length > 0);
const { isLicensed, isResolved, ensureLicenseLoaded } = useEvaluationsLicense();

const showSetupPanel = computed(
	() => setupPanelStore.isFeatureEnabled && selectedTab.value === 'setup',
);
const showEvaluationsPanel = computed(
	() =>
		isEvaluationsWizardSidepanelEnabled.value &&
		hasAiRootNode.value &&
		selectedTab.value === 'evaluations' &&
		isResolved.value &&
		isLicensed.value,
);
const showEvaluationsPaywall = computed(
	() =>
		isEvaluationsWizardSidepanelEnabled.value &&
		hasAiRootNode.value &&
		selectedTab.value === 'evaluations' &&
		isResolved.value &&
		!isLicensed.value,
);

// Tab bar visibility used to track only the setup panel; now it also needs to
// stay shown when the evaluations tab is available, otherwise the user has no
// way to switch back.
const showTabs = computed(
	() =>
		isSetupPanelEnabled.value || (isEvaluationsWizardSidepanelEnabled.value && hasAiRootNode.value),
);

const node = computed<INodeUi | undefined>(() => {
	if (!experimentalNdvStore.isNdvInFocusPanelEnabled || resolvedParameter.value) {
		return resolvedParameter.value?.node;
	}

	const selected: CanvasNode | undefined = vueFlow.getSelectedNodes.value[0];

	return selected?.data?.render.type === CanvasNodeRenderType.Default
		? (workflowDocumentStore?.value?.allNodes ?? []).find((n) => n.id === selected.id)
		: undefined;
});

const labelOverrides = computed(() => {
	const focusLabel = resolvedParameter.value?.parameter.displayName ?? node.value?.name;
	return focusLabel ? { focus: focusLabel } : undefined;
});

function handleKeydown(event: KeyboardEvent) {
	if (event.key === 's' && deviceSupport.isCtrlKeyPressed(event)) {
		event.stopPropagation();
		event.preventDefault();
	}
}

const registerKeyboardListener = () => {
	document.addEventListener('keydown', handleKeydown, true);
};

const unregisterKeyboardListener = () => {
	document.removeEventListener('keydown', handleKeydown, true);
};

watch(
	() => focusPanelStore.focusPanelActive,
	(newValue) => {
		if (newValue) {
			registerKeyboardListener();
		} else {
			unregisterKeyboardListener();
		}
	},
	{ immediate: true },
);

watch(activeElement, (active) => {
	if (!node.value || !active || !wrapperRef.value?.contains(active)) {
		return;
	}

	const path = active.closest('.parameter-input')?.getAttribute('data-parameter-path');

	if (!path) {
		return;
	}

	telemetry.track('User focused focus panel', {
		node_id: node.value.id,
		node_type: node.value.type,
		parameter_path: path,
	});
});

function onResize(event: ResizeData) {
	focusPanelStore.updateWidth(event.width);
}

const onResizeThrottle = useThrottleFn(onResize, 10);

function onContextMenuAction(action: ContextMenuAction, nodeIds: string[]) {
	emit('contextMenuAction', action, nodeIds);
}

onMounted(() => {
	void ensureLicenseLoaded();
});

onBeforeUnmount(() => {
	unregisterKeyboardListener();
});
</script>

<template>
	<div
		v-if="focusPanelActive"
		ref="wrapper"
		data-test-id="focus-sidebar"
		:class="[
			$style.wrapper,
			'ignore-key-press-canvas',
			{ [$style.isNdvInFocusPanelEnabled]: experimentalNdvStore.isNdvInFocusPanelEnabled },
		]"
		@keydown.stop
	>
		<N8nResizeWrapper
			:width="focusPanelWidth"
			:supported-directions="['left']"
			:min-width="isSetupPanelEnabled ? 420 : 300"
			:max-width="experimentalNdvStore.isNdvInFocusPanelEnabled ? undefined : 1000"
			:grid-size="8"
			:style="{ width: `${focusPanelWidth}px` }"
			@resize="onResizeThrottle"
		>
			<div :class="$style.container">
				<div v-if="showTabs">
					<FocusSidebarTabs v-model="selectedTab" :tab-labels="labelOverrides" />
				</div>
				<div v-if="showSetupPanel" :class="$style['setup-panel-wrapper']">
					<SetupPanel />
				</div>
				<div v-else-if="showEvaluationsPanel" :class="$style['setup-panel-wrapper']">
					<EvaluationsWizardSidepanel />
				</div>
				<div v-else-if="showEvaluationsPaywall" :class="$style['setup-panel-wrapper']">
					<EvaluationsPaywall />
				</div>
				<FocusPanel
					v-else
					:is-canvas-read-only="isCanvasReadOnly"
					@context-menu-action="onContextMenuAction"
				/>
			</div>
		</N8nResizeWrapper>
	</div>
</template>

<style lang="scss" module>
.wrapper {
	display: flex;
	flex-direction: row;
	flex-wrap: nowrap;
	border-left: 1px solid var(--color--foreground);
	background: var(--color--background--light-3);
	overflow: hidden;
	height: 100%;
	flex-grow: 0;
	flex-shrink: 0;
}

.container {
	display: flex;
	flex-direction: column;
	height: 100%;
}

.setup-panel-wrapper {
	display: flex;
	flex-direction: column;
	height: calc(100% - 36px);
	width: 100%;
}
</style>
