<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from 'vue';

import { usePreferences } from '@easyflow/preferences';

import {
  sanitizeTinyflowDataForReadonlyPreview,
  Tinyflow,
} from '@tinyflow-ai/vue';
import { ElEmpty, ElSkeleton } from 'element-plus';

import { $t } from '#/locales';
import { getCustomNode } from '#/views/ai/workflow/customNode';

import '@tinyflow-ai/vue/dist/index.css';

const props = defineProps<{
  content?: null | string;
  description?: null | string;
  title?: null | string;
}>();

const { isDark } = usePreferences();
const customNodes = ref<null | Record<string, any>>(null);
const tinyflowRef = ref<InstanceType<typeof Tinyflow> | null>(null);

const parsedData = computed(() => {
  return sanitizeTinyflowDataForReadonlyPreview(props.content || '');
});

const hasSnapshot = computed(() => !!String(props.content || '').trim());
const parseFailed = computed(() => parsedData.value === undefined);
const canRender = computed(
  () => !!customNodes.value && parsedData.value && !parseFailed.value,
);
const tinyflowKey = computed(
  () => `${isDark.value ? 'dark' : 'light'}:${String(props.content || '')}`,
);

onMounted(async () => {
  try {
    customNodes.value = await getCustomNode({});
  } catch {
    customNodes.value = {};
  }
});

watch(
  () => [customNodes.value, parsedData.value, isDark.value],
  async ([nodes, data]) => {
    if (!nodes || !data || data === undefined) {
      return;
    }
    await nextTick();
    requestAnimationFrame(() => {
      void tinyflowRef.value?.fitView({
        duration: 0,
        padding: 0.18,
      });
    });
  },
  {
    immediate: true,
  },
);
</script>

<template>
  <div class="workflow-snapshot-preview">
    <div class="workflow-snapshot-preview__meta">
      <div class="workflow-snapshot-preview__title">
        {{ title || $t('approval.message.workflowSnapshotUntitled') }}
      </div>
      <div v-if="description" class="workflow-snapshot-preview__description">
        {{ description }}
      </div>
    </div>

    <div v-if="!customNodes" class="workflow-snapshot-preview__loading">
      <ElSkeleton animated :rows="6" />
    </div>

    <ElEmpty
      v-else-if="!hasSnapshot"
      :description="$t('approval.message.workflowSnapshotMissing')"
      class="workflow-snapshot-preview__empty"
    />

    <ElEmpty
      v-else-if="parseFailed"
      :description="$t('approval.message.workflowSnapshotParseFailed')"
      class="workflow-snapshot-preview__empty"
    />

    <div v-else-if="canRender" class="workflow-snapshot-preview__canvas">
      <Tinyflow
        ref="tinyflowRef"
        :key="tinyflowKey"
        class-name="workflow-snapshot-preview__tinyflow"
        :data="parsedData"
        :theme="isDark ? 'dark' : 'light'"
        :custom-nodes="customNodes || undefined"
        :readonly="true"
        :hide-bottom-dock="true"
      />
    </div>
  </div>
</template>

<style scoped>
.workflow-snapshot-preview {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.workflow-snapshot-preview__meta {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.workflow-snapshot-preview__title {
  font-size: 15px;
  font-weight: 600;
  color: hsl(var(--foreground));
}

.workflow-snapshot-preview__description {
  font-size: 13px;
  line-height: 1.6;
  color: hsl(var(--muted-foreground));
}

.workflow-snapshot-preview__loading,
.workflow-snapshot-preview__empty,
.workflow-snapshot-preview__canvas {
  border: 1px solid hsl(var(--divider-faint) / 0.54);
  border-radius: 20px;
  background: hsl(var(--surface-contrast-soft) / 0.9);
}

.workflow-snapshot-preview__loading,
.workflow-snapshot-preview__empty {
  padding: 20px;
}

.workflow-snapshot-preview__canvas {
  overflow: hidden;
  min-height: 520px;
}

:deep(.workflow-snapshot-preview__tinyflow) {
  height: 520px;
  width: 100%;
}
</style>
