<script setup lang="ts">
import type { FormInstance } from 'element-plus';

import type {
  ChatImageAttachment,
  ChatTimelineItem,
  ChatTimelineMessageItem,
} from '@easyflow/common-ui';

import type {
  WorkflowExecutionStepStatus,
  WorkflowExecutionStepView,
} from './workflowExecutionDetails';

import {
  computed,
  nextTick,
  onBeforeUnmount,
  onMounted,
  ref,
  watch,
} from 'vue';
import { useRoute } from 'vue-router';

import { ChatTimeline, defaultAssistantAvatar } from '@easyflow/common-ui';
import { useUserStore } from '@easyflow/stores';

import {
  ArrowDown,
  ArrowLeft,
  ArrowUp,
  Promotion,
  RefreshLeft,
  Share,
  Tickets,
} from '@element-plus/icons-vue';
import { useResizeObserver } from '@vueuse/core';
import {
  ElAvatar,
  ElButton,
  ElDescriptions,
  ElDescriptionsItem,
  ElDrawer,
  ElEmpty,
  ElForm,
  ElIcon,
  ElSkeleton,
  ElTag,
  ElTooltip,
} from 'element-plus';

import { api, SseClient } from '#/api/request';
import workflowIcon from '#/assets/ai/workflow/workflowIcon.png';
import { $t } from '#/locales';
import { router } from '#/router';
import { navigateBackToList } from '#/router/list-return-context';
import { resolveAgentChatIdentity } from '#/utils/agent-chat-cache';
import { copyTextWithFeedback } from '#/utils/clipboard-feedback';
import { buildAbsoluteAppRouteUrl } from '#/utils/share-route-context';
import { resolveWorkflowShareFailureReason } from '#/utils/workflow-share-context';

import {
  finalizeWorkflowExecutionSteps,
  formatExecutionValue,
  hydrateWorkflowExecutionSteps,
  reduceWorkflowExecutionSteps,
} from './workflowExecutionDetails';
import WorkflowFinalOutput from './WorkflowFinalOutput.vue';
import WorkflowFormItem from './WorkflowFormItem.vue';
import { resolveWorkflowFormParameters } from './workflowFormParameters';
import {
  buildWorkflowFormParameterSummaries,
  buildWorkflowFormSubmissionImages,
} from './workflowFormPresentation';
import {
  buildWorkflowRunDraftKey,
  getWorkflowRunDraftStorage,
  hasWorkflowRunDraftContent,
  readWorkflowRunDraft,
  removeWorkflowRunDraft,
  writeWorkflowRunDraft,
} from './workflowRunDraft';
import {
  formatWorkflowElapsed,
  formatWorkflowProgressLabel,
  summarizeWorkflowActiveNodes,
} from './workflowRunProgress';

interface WorkflowStreamEnvelope {
  data?: Record<string, any>;
  eventId: string;
  executeId?: string;
  sequence: number;
  type: string;
}

const props = withDefaults(
  defineProps<{
    shareMode?: boolean;
  }>(),
  {
    shareMode: false,
  },
);

const MAX_COLLAPSED_PARAMETER_COUNT = 4;
const PRIMARY_PARAMETER_COUNT = 4;
const DRAFT_SAVE_DELAY_MS = 300;
const route = useRoute();
const userStore = useUserStore();
const streamClient = new SseClient();
const loading = ref(true);
const loadError = ref('');
const descriptor = ref<Record<string, any>>({});
const workflowId = ref<string>();
const timelineItems = ref<ChatTimelineItem[]>([]);
const question = ref('');
const running = ref(false);
const stopping = ref(false);
const executeId = ref('');
const runStatusKey = ref('');
const parametersLocked = ref(false);
const parametersExpanded = ref(false);
const moreParametersExpanded = ref(false);
const parametersValidating = ref(false);
const parameterChipsRef = ref<HTMLElement>();
const parameterChipsWidth = ref(0);
const extraValues = ref<Record<string, any>>({});
const defaultExtraValues = ref<Record<string, any>>({});
const extraFormRef = ref<FormInstance>();
const waitingConfirmation = ref<Record<string, any>>();
const confirmValues = ref<Record<string, any>>({});
const confirmFormRef = ref<FormInstance>();
const confirmSubmittingAction = ref<'' | 'confirm' | 'reject'>('');
const confirmError = ref('');
const detailVisible = ref(false);
const detailLoading = ref(false);
const detailLoadError = ref('');
const executionDetail = ref<Record<string, any>>();
const liveExecutionSteps = ref<WorkflowExecutionStepView[]>([]);
const expandedExecutionStepKeys = ref<string[]>([]);
const detailExpansionTouched = ref(false);
const executionState = ref<
  'cancelled' | 'completed' | 'failed' | 'idle' | 'running' | 'waiting'
>('idle');
const executionStartedAt = ref<number>();
const executionElapsed = ref<number>();
const sharing = ref(false);
const manualAbort = ref(false);
const lastRunningNodeName = ref('');
let progressStatusTimer = 0;
let draftSaveTimer = 0;
let draftReady = false;
let userMessageSequence = 0;

const formParameters = computed(() =>
  resolveWorkflowFormParameters(descriptor.value),
);
const additionalParameters = computed(() =>
  formParameters.value.filter(
    (parameter: any) => parameter.name !== 'user_input',
  ),
);
const parameterSummaries = computed(() =>
  buildWorkflowFormParameterSummaries(
    additionalParameters.value,
    extraValues.value,
  ),
);
const primaryParameters = computed(() => {
  const parameters = additionalParameters.value;
  if (parameters.length <= PRIMARY_PARAMETER_COUNT) {
    return parameters;
  }

  const requiredCount = parameters.filter(
    (parameter: any) => parameter.required === true,
  ).length;
  let optionalSlots = Math.max(0, PRIMARY_PARAMETER_COUNT - requiredCount);
  return parameters.filter((parameter: any) => {
    if (parameter.required === true) {
      return true;
    }
    if (optionalSlots <= 0) {
      return false;
    }
    optionalSlots -= 1;
    return true;
  });
});
const moreParameters = computed(() => {
  const primaryParameterSet = new Set(primaryParameters.value);
  return additionalParameters.value.filter(
    (parameter: any) => !primaryParameterSet.has(parameter),
  );
});
const collapsedParameterLimit = computed(() => {
  const width = parameterChipsWidth.value;
  if (width <= 0 || width >= 560) {
    return MAX_COLLAPSED_PARAMETER_COUNT;
  }
  if (width >= 400) {
    return 3;
  }
  if (width >= 260) {
    return 2;
  }
  return 1;
});
const visibleParameterSummaries = computed(() =>
  parameterSummaries.value.slice(0, collapsedParameterLimit.value),
);
const hiddenParameterSummaryCount = computed(() =>
  Math.max(
    0,
    parameterSummaries.value.length - visibleParameterSummaries.value.length,
  ),
);
const confirmParameters = computed(() => {
  const parameters = Array.isArray(waitingConfirmation.value?.parameters)
    ? waitingConfirmation.value?.parameters
    : [];
  return parameters.filter(
    (parameter: any) => parameter.formType !== 'confirm',
  );
});
const confirmKey = computed(() => {
  const parameters = Array.isArray(waitingConfirmation.value?.parameters)
    ? waitingConfirmation.value?.parameters
    : [];
  return (
    parameters.find((parameter: any) => parameter.formType === 'confirm')
      ?.name || ''
  );
});
const composerDisabled = computed(
  () =>
    loading.value ||
    parametersValidating.value ||
    running.value ||
    Boolean(waitingConfirmation.value),
);
const canSend = computed(
  () => question.value.trim().length > 0 && !composerDisabled.value,
);
const shareable = computed(() => descriptor.value.shareable === true);
const emptyText = computed(
  () => descriptor.value.description || '输入问题开始运行',
);
const persistedExecutionSteps = computed(() =>
  hydrateWorkflowExecutionSteps(executionDetail.value?.steps),
);
const executionSteps = computed(() =>
  liveExecutionSteps.value.length > 0
    ? liveExecutionSteps.value
    : persistedExecutionSteps.value,
);
const expandedExecutionStepKeySet = computed(
  () => new Set(expandedExecutionStepKeys.value),
);
const detailStatusText = computed(() => {
  const labels = {
    cancelled: '已中止',
    completed: '已完成',
    failed: '运行失败',
    idle: '等待运行',
    running: '运行中',
    waiting: '等待确认',
  };
  if (executionState.value !== 'idle') {
    return labels[executionState.value];
  }
  return statusText(executionDetail.value?.record?.status);
});
const detailDurationText = computed(() => {
  if (
    executionState.value === 'running' ||
    executionState.value === 'waiting'
  ) {
    return '进行中';
  }
  const persistedDuration = executionDetail.value?.record?.execTime;
  const duration =
    persistedDuration === null || persistedDuration === undefined
      ? executionElapsed.value
      : persistedDuration;
  return duration === undefined ? '—' : `${duration} ms`;
});

useResizeObserver(parameterChipsRef, ([entry]) => {
  parameterChipsWidth.value = entry?.contentRect.width || 0;
});

watch([question, extraValues], scheduleDraftSave, { deep: true });

onMounted(() => {
  window.addEventListener('pagehide', persistDraft);
  void loadPage();
});

onBeforeUnmount(() => {
  window.removeEventListener('pagehide', persistDraft);
  clearDraftSaveTimer();
  persistDraft();
  manualAbort.value = true;
  streamClient.abort();
  clearProgressStatusTimer();
});

async function loadPage() {
  draftReady = false;
  loading.value = true;
  loadError.value = '';
  try {
    workflowId.value = props.shareMode
      ? await resolveSharedWorkflowId()
      : String(route.query.id || '').trim();
    if (!workflowId.value) {
      throw new Error('工作流不存在');
    }
    const response = await api.get('/api/v1/workflowChat/descriptor', {
      params: { workflowId: workflowId.value },
    });
    descriptor.value = response.data || {};
    initializeAdditionalValues();
    restoreDraft();
    await nextTick();
    draftReady = true;
  } catch (error: any) {
    loadError.value = error?.message || '工作流加载失败';
  } finally {
    loading.value = false;
  }
}

async function resolveSharedWorkflowId() {
  try {
    const response = await api.get('/api/v1/workflowShare/resolve');
    return String(response.data?.workflowId || '');
  } catch (error) {
    await router.replace({
      name: 'WorkflowShareExpired',
      query: {
        reason: resolveWorkflowShareFailureReason(error),
      },
    });
    throw error;
  }
}

function initializeAdditionalValues() {
  const values: Record<string, any> = {};
  for (const parameter of additionalParameters.value) {
    const defaultValue = parameter.defaultValue;
    if (
      parameter.contentType === 'file' &&
      (defaultValue === null ||
        defaultValue === undefined ||
        defaultValue === '')
    ) {
      values[parameter.name] = [];
    } else if (Array.isArray(defaultValue)) {
      values[parameter.name] = [...defaultValue];
    } else if (defaultValue && typeof defaultValue === 'object') {
      values[parameter.name] = { ...defaultValue };
    } else {
      values[parameter.name] = defaultValue ?? '';
    }
  }
  defaultExtraValues.value = values;
  extraValues.value = values;
  parametersLocked.value = false;
  parametersExpanded.value = buildWorkflowFormParameterSummaries(
    additionalParameters.value,
    values,
  ).some((summary) => summary.required && !summary.ready);
  moreParametersExpanded.value = false;
  parametersValidating.value = false;
}

function draftKey() {
  if (!workflowId.value) {
    return '';
  }
  return buildWorkflowRunDraftKey(
    workflowId.value,
    resolveAgentChatIdentity(userStore.userInfo),
    props.shareMode,
  );
}

function restoreDraft() {
  const draft = readWorkflowRunDraft(
    getWorkflowRunDraftStorage(),
    draftKey(),
    additionalParameters.value,
  );
  if (!draft) {
    return false;
  }
  question.value = draft.question;
  extraValues.value = { ...extraValues.value, ...draft.values };
  parametersExpanded.value = buildWorkflowFormParameterSummaries(
    additionalParameters.value,
    extraValues.value,
  ).some((summary) => summary.required && !summary.ready);
  return hasWorkflowRunDraftContent(
    question.value,
    extraValues.value,
    defaultExtraValues.value,
  );
}

function clearDraftSaveTimer() {
  if (draftSaveTimer) {
    window.clearTimeout(draftSaveTimer);
    draftSaveTimer = 0;
  }
}

function scheduleDraftSave() {
  if (!draftReady) {
    return;
  }
  clearDraftSaveTimer();
  draftSaveTimer = window.setTimeout(() => {
    draftSaveTimer = 0;
    persistDraft();
  }, DRAFT_SAVE_DELAY_MS);
}

function persistDraft() {
  if (!draftReady) {
    return;
  }
  const storage = getWorkflowRunDraftStorage();
  const key = draftKey();
  if (
    !hasWorkflowRunDraftContent(
      question.value,
      extraValues.value,
      defaultExtraValues.value,
    )
  ) {
    removeWorkflowRunDraft(storage, key);
    return;
  }
  writeWorkflowRunDraft(storage, key, {
    question: question.value,
    values: extraValues.value,
  });
}

async function backToWorkflowList() {
  await navigateBackToList(
    router,
    route.query,
    ['/ai/workflow'],
    '/ai/workflow',
  );
}

async function validateAdditionalInputs() {
  if (parametersLocked.value) {
    return true;
  }
  if (additionalParameters.value.length === 0) {
    return true;
  }
  if (parametersValidating.value) {
    return false;
  }
  parametersExpanded.value = true;
  await nextTick();
  parametersValidating.value = true;
  try {
    return (await extraFormRef.value?.validate().catch(() => false)) ?? true;
  } finally {
    parametersValidating.value = false;
  }
}

function appendStatus(
  label: string,
  status: 'done' | 'running',
  statusKey: string,
) {
  const existing = timelineItems.value.find(
    (item) => item.type === 'status' && item.statusKey === statusKey,
  );
  if (existing?.type === 'status') {
    existing.label = label;
    existing.status = status;
    timelineItems.value = [...timelineItems.value];
    return;
  }
  timelineItems.value.push({
    id: `status-${statusKey}-${Date.now()}`,
    icon: 'none',
    label,
    presentation: 'inline',
    status,
    statusKey,
    tone: 'muted',
    type: 'status',
  });
}

function appendUserMessage(
  content: string,
  images: ChatImageAttachment[] = [],
) {
  const id = `user-${Date.now()}-${++userMessageSequence}`;
  timelineItems.value.push({
    id,
    images: images.length > 0 ? images : undefined,
    role: 'user',
    status: 'done',
    parts: content
      ? [
          {
            content,
            id: `${id}-text`,
            type: 'text',
          },
        ]
      : [],
    type: 'message',
  });
}

function appendError(message: string, id = `error-${Date.now()}`) {
  if (
    timelineItems.value.some((item) => item.type === 'error' && item.id === id)
  ) {
    return;
  }
  timelineItems.value.push({
    id,
    message: message || '工作流执行失败',
    type: 'error',
  });
}

function appendFinalOutput(output: unknown, eventId: string) {
  timelineItems.value.push({
    customType: 'workflow-final-output',
    data: output,
    id: `final-output-${eventId}`,
    type: 'custom',
  });
}

function clearProgressStatusTimer() {
  if (progressStatusTimer) {
    window.clearTimeout(progressStatusTimer);
    progressStatusTimer = 0;
  }
}

function activeNodeSummary() {
  return summarizeWorkflowActiveNodes(
    liveExecutionSteps.value,
    lastRunningNodeName.value,
  );
}

function progressLabel(prefix: string) {
  return formatWorkflowProgressLabel(
    prefix,
    liveExecutionSteps.value,
    lastRunningNodeName.value,
  );
}

function scheduleRunningStatus() {
  if (!running.value || executionState.value !== 'running') {
    return;
  }
  clearProgressStatusTimer();
  progressStatusTimer = window.setTimeout(() => {
    progressStatusTimer = 0;
    appendStatus(
      progressLabel(stopping.value ? '正在中止' : '正在运行'),
      'running',
      runStatusKey.value,
    );
  }, 140);
}

function markConfirmationStep(status: 'running' | 'waiting') {
  for (
    let index = liveExecutionSteps.value.length - 1;
    index >= 0;
    index -= 1
  ) {
    const step = liveExecutionSteps.value[index];
    if (!step || (step.status !== 'running' && step.status !== 'waiting')) {
      continue;
    }
    const next = [...liveExecutionSteps.value];
    next[index] = { ...step, status };
    liveExecutionSteps.value = next;
    return;
  }
}

async function handleSend() {
  const content = question.value.trim();
  if (!content || composerDisabled.value || !workflowId.value) {
    return;
  }
  if (!(await validateAdditionalInputs())) {
    return;
  }
  const images = buildWorkflowFormSubmissionImages(
    additionalParameters.value,
    extraValues.value,
  );
  parametersLocked.value = true;
  appendUserMessage(content, images);
  question.value = '';
  parametersExpanded.value = false;
  running.value = true;
  executionState.value = 'running';
  executionStartedAt.value = Date.now();
  executionElapsed.value = undefined;
  executeId.value = '';
  runStatusKey.value = `run-${Date.now()}-${userMessageSequence}`;
  executionDetail.value = undefined;
  detailLoadError.value = '';
  liveExecutionSteps.value = [];
  expandedExecutionStepKeys.value = [];
  detailExpansionTouched.value = false;
  waitingConfirmation.value = undefined;
  confirmSubmittingAction.value = '';
  confirmError.value = '';
  lastRunningNodeName.value = '';
  manualAbort.value = false;

  void streamClient.post(
    '/api/v1/workflowChat/run',
    {
      workflowId: workflowId.value,
      variables: {
        ...extraValues.value,
        user_input: content,
      },
    },
    {
      onError: (error) => {
        if (manualAbort.value) {
          return;
        }
        finishExecution(
          'failed',
          error?.message || '工作流执行失败',
          undefined,
          `stream-error-${Date.now()}`,
        );
      },
      onFinished: () => {
        if (running.value && !manualAbort.value) {
          finishExecution(
            'failed',
            '运行连接已结束，请重试',
            undefined,
            `stream-finished-${Date.now()}`,
          );
        }
      },
      onMessage: (message) => {
        if (!message.data) {
          return;
        }
        const event = parseStreamEvent(message.data);
        if (event) {
          handleStreamEvent(event);
        }
      },
    },
  );
}

function parseStreamEvent(raw: string): null | WorkflowStreamEnvelope {
  try {
    return JSON.parse(raw) as WorkflowStreamEnvelope;
  } catch {
    return null;
  }
}

function handleStreamEvent(event: WorkflowStreamEnvelope) {
  updateLiveExecutionSteps(event);
  const data = event.data || {};
  switch (event.type) {
    case 'execution_cancelled': {
      finishExecution('cancelled');
      break;
    }
    case 'execution_error': {
      appendError(
        data.message || '工作流执行失败',
        `execution-error-${event.executeId}`,
      );
      break;
    }
    case 'execution_failed': {
      finishExecution('failed', data.message);
      break;
    }
    case 'execution_finished': {
      finishExecution('completed', undefined, data.output, event.eventId);
      break;
    }
    case 'execution_started': {
      executeId.value = String(event.executeId || data.executeId || '');
      executionState.value = 'running';
      appendStatus('准备运行…', 'running', runStatusKey.value);
      break;
    }
    case 'execution_status': {
      if (data.status === 'RUNNING') {
        waitingConfirmation.value = undefined;
        confirmError.value = '';
        executionState.value = 'running';
        markConfirmationStep('running');
        scheduleRunningStatus();
      }
      break;
    }
    case 'execution_waiting': {
      clearProgressStatusTimer();
      waitingConfirmation.value = data;
      executionState.value = 'waiting';
      confirmSubmittingAction.value = '';
      confirmError.value = '';
      initializeConfirmValues(data.parameters);
      markConfirmationStep('waiting');
      appendStatus(progressLabel('等待确认'), 'running', runStatusKey.value);
      break;
    }
    case 'node_finished': {
      scheduleRunningStatus();
      break;
    }
    case 'node_started': {
      lastRunningNodeName.value = String(
        data.nodeName || lastRunningNodeName.value,
      );
      scheduleRunningStatus();
      break;
    }
  }
}

function finishExecution(
  terminalStatus: 'cancelled' | 'completed' | 'failed',
  message?: string,
  output?: unknown,
  eventId = executeId.value || String(Date.now()),
) {
  const failedNodeName = activeNodeSummary();
  clearProgressStatusTimer();
  running.value = false;
  stopping.value = false;
  waitingConfirmation.value = undefined;
  confirmSubmittingAction.value = '';
  confirmError.value = '';
  executionState.value = terminalStatus;
  finalizeLiveExecutionSteps(terminalStatus);
  const elapsedText = formatWorkflowElapsed(executionElapsed.value);
  let label = elapsedText ? `运行完成 · ${elapsedText}` : '运行完成';
  if (terminalStatus === 'cancelled') {
    label = '已中止';
  } else if (terminalStatus === 'failed') {
    label = failedNodeName ? `运行失败 · ${failedNodeName}` : '运行失败';
  }
  appendStatus(label, 'done', runStatusKey.value || `run-${eventId}`);
  if (terminalStatus === 'completed') {
    appendFinalOutput(output, eventId);
  }
  if (terminalStatus === 'failed' && message) {
    appendError(message, `terminal-error-${executeId.value}`);
  }
  timelineItems.value = [...timelineItems.value];
  if (executeId.value) {
    window.setTimeout(() => void loadExecutionDetail(), 320);
  }
}

function updateLiveExecutionSteps(event: WorkflowStreamEnvelope) {
  const next = reduceWorkflowExecutionSteps(liveExecutionSteps.value, event);
  if (next === liveExecutionSteps.value) {
    return;
  }
  liveExecutionSteps.value = next;
  if (event.type === 'node_started') {
    const latestStep = next[next.length - 1];
    lastRunningNodeName.value =
      latestStep?.nodeName || lastRunningNodeName.value;
  }
  if (
    event.type === 'node_started' &&
    !detailExpansionTouched.value &&
    next.length > 0
  ) {
    expandedExecutionStepKeys.value = [next[next.length - 1]!.key];
  }
}

function finalizeLiveExecutionSteps(
  status: 'cancelled' | 'completed' | 'failed',
) {
  const finishedAt = Date.now();
  liveExecutionSteps.value = finalizeWorkflowExecutionSteps(
    liveExecutionSteps.value,
    status,
    finishedAt,
  );
  executionElapsed.value =
    executionStartedAt.value === undefined
      ? undefined
      : Math.max(0, finishedAt - executionStartedAt.value);
}

function initializeConfirmValues(parameters: unknown) {
  const values: Record<string, any> = {};
  for (const parameter of Array.isArray(parameters) ? parameters : []) {
    if (parameter.formType === 'confirm') {
      continue;
    }
    values[parameter.name] = parameter.defaultValue ?? '';
  }
  confirmValues.value = values;
}

async function resumeExecution(confirmed: boolean) {
  if (!executeId.value || !confirmKey.value || confirmSubmittingAction.value) {
    return;
  }
  confirmSubmittingAction.value = confirmed ? 'confirm' : 'reject';
  confirmError.value = '';
  try {
    if (
      confirmed &&
      !(await confirmFormRef.value?.validate().catch(() => false))
    ) {
      return;
    }
    await api.post('/api/v1/workflowChat/resume', {
      executeId: executeId.value,
      confirmParams: {
        [confirmKey.value]: confirmed ? 'yes' : 'no',
        ...(confirmed ? confirmValues.value : {}),
      },
    });
    waitingConfirmation.value = undefined;
    executionState.value = 'running';
    markConfirmationStep('running');
    appendStatus(progressLabel('正在运行'), 'running', runStatusKey.value);
  } catch (error: any) {
    confirmError.value = error?.message || '提交失败，请重试';
  } finally {
    confirmSubmittingAction.value = '';
  }
}

async function stopExecution() {
  if (!running.value || stopping.value) {
    return;
  }
  stopping.value = true;
  clearProgressStatusTimer();
  appendStatus(progressLabel('正在中止'), 'running', runStatusKey.value);
  try {
    if (executeId.value) {
      await api.post('/api/v1/workflowChat/cancel', {
        executeId: executeId.value,
      });
    }
    manualAbort.value = true;
    streamClient.abort();
    finishExecution('cancelled');
  } catch (error: any) {
    stopping.value = false;
    appendStatus(progressLabel('正在运行'), 'running', runStatusKey.value);
    appendError(
      error?.message || '中止失败，请重试',
      `cancel-error-${executeId.value || Date.now()}`,
    );
  }
}

async function resetConversation() {
  if (running.value) {
    await stopExecution();
    if (running.value) {
      return;
    }
  }
  draftReady = false;
  clearDraftSaveTimer();
  removeWorkflowRunDraft(getWorkflowRunDraftStorage(), draftKey());
  manualAbort.value = true;
  streamClient.abort();
  timelineItems.value = [];
  liveExecutionSteps.value = [];
  expandedExecutionStepKeys.value = [];
  detailExpansionTouched.value = false;
  clearProgressStatusTimer();
  executeId.value = '';
  runStatusKey.value = '';
  question.value = '';
  executionDetail.value = undefined;
  executionState.value = 'idle';
  executionStartedAt.value = undefined;
  executionElapsed.value = undefined;
  detailLoadError.value = '';
  waitingConfirmation.value = undefined;
  confirmSubmittingAction.value = '';
  confirmError.value = '';
  lastRunningNodeName.value = '';
  initializeAdditionalValues();
  await nextTick();
  draftReady = true;
}

async function loadExecutionDetail() {
  if (!executeId.value) {
    return;
  }
  detailLoading.value = true;
  detailLoadError.value = '';
  try {
    const response = await api.get('/api/v1/workflowChat/execution', {
      params: { executeId: executeId.value },
    });
    executionDetail.value = response.data;
  } catch (error: any) {
    detailLoadError.value = error?.message || '运行详情加载失败';
  } finally {
    detailLoading.value = false;
  }
}

async function openExecutionDetail() {
  detailVisible.value = true;
  if (!running.value) {
    await loadExecutionDetail();
  }
}

async function shareWorkflow() {
  if (!workflowId.value || sharing.value) {
    return;
  }
  sharing.value = true;
  try {
    const response = await api.post('/api/v1/workflowShare/url/create', {
      workflowId: workflowId.value,
    });
    const shareKey = response.data?.shareKey;
    if (!shareKey) {
      return;
    }
    const shareUrl = buildAbsoluteAppRouteUrl(
      router.resolve({
        name: 'WorkflowShare',
        query: { shareKey },
      }).href,
    );
    await copyTextWithFeedback(shareUrl, '分享链接已复制', '复制失败');
  } finally {
    sharing.value = false;
  }
}

async function copyMessage(item: ChatTimelineMessageItem) {
  const content = item.parts
    .filter((part) => part.type === 'text')
    .map((part) => part.content)
    .join('\n\n')
    .trim();
  if (!content) {
    return false;
  }
  return copyTextWithFeedback(content, '已复制', '复制失败');
}

function statusText(status: unknown) {
  const labels: Record<string, string> = {
    '0': '等待运行',
    '1': '运行中',
    '5': '等待确认',
    '10': '运行异常',
    '20': '已完成',
    '21': '运行失败',
    '22': '已中止',
  };
  return labels[String(status)] || '未知';
}

function toggleExecutionStep(stepKey: string) {
  detailExpansionTouched.value = true;
  const next = new Set(expandedExecutionStepKeys.value);
  if (next.has(stepKey)) {
    next.delete(stepKey);
  } else {
    next.add(stepKey);
  }
  expandedExecutionStepKeys.value = [...next];
}

function isExecutionStepExpanded(stepKey: string) {
  return expandedExecutionStepKeySet.value.has(stepKey);
}

function executionStepStatusText(status: WorkflowExecutionStepStatus) {
  const labels: Record<WorkflowExecutionStepStatus, string> = {
    cancelled: '已中止',
    completed: '已完成',
    failed: '失败',
    running: '运行中',
    waiting: '等待确认',
  };
  return labels[status];
}

function executionStepDuration(step: WorkflowExecutionStepView) {
  return step.duration === undefined ? '' : `${step.duration} ms`;
}

function executionTraceText(
  trace: WorkflowExecutionStepView['traces'][number],
) {
  return trace.outcome === 'matched'
    ? `进入 ${trace.targetNodeName}`
    : `未进入 ${trace.targetNodeName}`;
}
</script>

<template>
  <div
    class="workflow-chat"
    :class="{ 'is-share': shareMode }"
    v-loading="loading"
  >
    <header class="workflow-chat__header">
      <div class="workflow-chat__identity">
        <ElButton
          v-if="!shareMode"
          :icon="ArrowLeft"
          circle
          text
          aria-label="返回工作流"
          @click="backToWorkflowList"
        />
        <ElAvatar
          :size="40"
          :src="descriptor.icon || workflowIcon"
          shape="square"
        />
        <div class="workflow-chat__heading">
          <div class="workflow-chat__title-row">
            <h1>{{ descriptor.title || '工作流' }}</h1>
            <ElTag
              size="small"
              :type="shareable ? 'success' : 'info'"
              effect="light"
            >
              {{ shareable ? '已发布' : '未发布' }}
            </ElTag>
          </div>
          <p v-if="descriptor.description">{{ descriptor.description }}</p>
        </div>
      </div>
      <div class="workflow-chat__actions">
        <ElTooltip v-if="!shareMode && shareable" content="复制分享链接">
          <ElButton
            :icon="Share"
            circle
            text
            :loading="sharing"
            aria-label="分享"
            @click="shareWorkflow"
          />
        </ElTooltip>
        <ElTooltip content="运行详情">
          <ElButton
            :icon="Tickets"
            circle
            text
            :disabled="!executeId"
            aria-label="运行详情"
            @click="openExecutionDetail"
          />
        </ElTooltip>
        <ElTooltip content="清空重来">
          <ElButton
            :icon="RefreshLeft"
            circle
            text
            aria-label="清空重来"
            @click="resetConversation"
          />
        </ElTooltip>
      </div>
    </header>

    <main class="workflow-chat__main">
      <div v-if="loading" class="workflow-chat__state">
        <ElSkeleton :rows="5" animated />
      </div>
      <div v-else-if="loadError" class="workflow-chat__state">
        <ElEmpty :description="loadError">
          <ElButton type="primary" @click="loadPage">重试</ElButton>
        </ElEmpty>
      </div>
      <template v-else>
        <ChatTimeline
          class="workflow-chat__timeline"
          :assistant-avatar="defaultAssistantAvatar"
          :items="timelineItems"
          :empty-text="emptyText"
          :empty-title="descriptor.title || '工作流'"
          :copy-action="copyMessage"
          :copyable="(item) => item.parts.some((part) => part.content)"
        >
          <template #custom-item="{ item }">
            <WorkflowFinalOutput
              v-if="item.customType === 'workflow-final-output'"
              :output="item.data"
            />
          </template>
        </ChatTimeline>

        <section
          v-if="waitingConfirmation"
          class="workflow-chat__form-card is-confirm"
          aria-label="工作流确认"
        >
          <div class="workflow-chat__form-heading">
            <h2>{{ waitingConfirmation.message || '请确认' }}</h2>
          </div>
          <ElForm
            v-if="confirmParameters.length > 0"
            ref="confirmFormRef"
            :model="confirmValues"
            label-position="top"
            @submit.prevent
          >
            <WorkflowFormItem
              :parameters="confirmParameters"
              :run-params="confirmValues"
              @update:run-params="confirmValues = $event"
            />
          </ElForm>
          <p
            v-if="confirmError"
            class="workflow-chat__confirm-error"
            role="alert"
          >
            {{ confirmError }}
          </p>
          <div class="workflow-chat__form-actions">
            <ElButton
              :loading="confirmSubmittingAction === 'reject'"
              :disabled="Boolean(confirmSubmittingAction)"
              @click="resumeExecution(false)"
            >
              拒绝
            </ElButton>
            <ElButton
              type="primary"
              :loading="confirmSubmittingAction === 'confirm'"
              :disabled="Boolean(confirmSubmittingAction)"
              @click="resumeExecution(true)"
            >
              确认
            </ElButton>
          </div>
        </section>

        <div class="workflow-chat__composer">
          <section
            class="workflow-chat__input-shell"
            aria-label="工作流运行输入"
          >
            <section
              v-if="additionalParameters.length > 0"
              class="workflow-chat__parameter-panel"
              :class="{ 'is-expanded': parametersExpanded }"
              :aria-label="$t('aiWorkflow.runInputForm')"
            >
              <div class="workflow-chat__parameter-summary">
                <div class="workflow-chat__parameter-heading">
                  <span>{{ $t('aiWorkflow.runInputForm') }}</span>
                </div>

                <div
                  v-if="!parametersExpanded"
                  ref="parameterChipsRef"
                  class="workflow-chat__parameter-chips"
                >
                  <button
                    v-for="summary in visibleParameterSummaries"
                    :key="summary.key"
                    class="workflow-chat__parameter-chip"
                    :class="{ 'is-pending': !summary.ready }"
                    type="button"
                    @click="parametersExpanded = true"
                  >
                    <span>{{ summary.label }}</span>
                    <strong>{{ summary.value }}</strong>
                  </button>
                  <button
                    v-if="hiddenParameterSummaryCount > 0"
                    class="workflow-chat__parameter-chip is-overflow"
                    type="button"
                    :aria-label="`还有 ${hiddenParameterSummaryCount} 个运行参数`"
                    @click="parametersExpanded = true"
                  >
                    +{{ hiddenParameterSummaryCount }}
                  </button>
                </div>

                <button
                  class="workflow-chat__parameter-toggle"
                  type="button"
                  :aria-expanded="parametersExpanded"
                  :aria-label="
                    parametersExpanded ? '收起运行参数' : '调整运行参数'
                  "
                  @click="parametersExpanded = !parametersExpanded"
                >
                  <span>{{ parametersExpanded ? '收起' : '调整' }}</span>
                  <ElIcon aria-hidden="true">
                    <ArrowUp v-if="parametersExpanded" />
                    <ArrowDown v-else />
                  </ElIcon>
                </button>
              </div>

              <Transition name="workflow-parameters">
                <div
                  v-show="parametersExpanded"
                  class="workflow-chat__parameter-editor"
                >
                  <p
                    v-if="descriptor.startFormMeta?.description"
                    class="workflow-chat__parameter-description"
                  >
                    {{ descriptor.startFormMeta.description }}
                  </p>
                  <ElForm
                    ref="extraFormRef"
                    class="workflow-chat__parameter-form"
                    :model="extraValues"
                    label-position="top"
                    scroll-to-error
                    @submit.prevent
                  >
                    <fieldset>
                      <div class="workflow-chat__parameter-grid">
                        <WorkflowFormItem
                          :disabled="composerDisabled || parametersLocked"
                          :parameters="primaryParameters"
                          :run-params="extraValues"
                          @update:run-params="extraValues = $event"
                        />
                      </div>

                      <section
                        v-if="moreParameters.length > 0"
                        class="workflow-chat__more-parameters"
                      >
                        <button
                          class="workflow-chat__more-parameters-toggle"
                          type="button"
                          :aria-expanded="moreParametersExpanded"
                          @click="
                            moreParametersExpanded = !moreParametersExpanded
                          "
                        >
                          <span>更多参数</span>
                          <small>{{ moreParameters.length }}</small>
                          <ElIcon aria-hidden="true">
                            <ArrowUp v-if="moreParametersExpanded" />
                            <ArrowDown v-else />
                          </ElIcon>
                        </button>

                        <Transition name="workflow-parameter-more">
                          <div
                            v-show="moreParametersExpanded"
                            class="workflow-chat__parameter-grid workflow-chat__more-parameters-grid"
                          >
                            <WorkflowFormItem
                              :disabled="composerDisabled || parametersLocked"
                              :parameters="moreParameters"
                              :run-params="extraValues"
                              @update:run-params="extraValues = $event"
                            />
                          </div>
                        </Transition>
                      </section>
                    </fieldset>
                  </ElForm>
                </div>
              </Transition>
            </section>

            <div class="workflow-chat__input-row">
              <textarea
                v-model="question"
                rows="1"
                :disabled="composerDisabled"
                placeholder="请描述这次要处理的问题…"
                aria-label="用户问题"
                @keydown.enter.exact.prevent="handleSend"
              ></textarea>
              <ElButton
                v-if="running"
                class="workflow-chat__send is-stop"
                type="primary"
                circle
                :loading="stopping"
                aria-label="中止运行"
                title="中止运行"
                @click="stopExecution"
              />
              <ElButton
                v-else
                class="workflow-chat__send"
                type="primary"
                circle
                :icon="Promotion"
                :loading="parametersValidating"
                :disabled="!canSend"
                aria-label="开始运行"
                title="开始运行"
                @click="handleSend"
              />
            </div>
          </section>
          <span>Enter 发送 · Shift + Enter 换行</span>
        </div>
      </template>
    </main>

    <ElDrawer
      v-model="detailVisible"
      title="运行详情"
      size="min(520px, 92vw)"
      append-to-body
    >
      <div v-loading="detailLoading" class="workflow-chat__detail">
        <ElDescriptions v-if="executeId" :column="1" border size="small">
          <ElDescriptionsItem label="状态">
            {{ detailStatusText }}
          </ElDescriptionsItem>
          <ElDescriptionsItem label="耗时">
            {{ detailDurationText }}
          </ElDescriptionsItem>
          <ElDescriptionsItem label="执行 ID">
            <span class="workflow-chat__detail-id">{{ executeId }}</span>
          </ElDescriptionsItem>
          <ElDescriptionsItem
            v-if="executionDetail?.record?.errorInfo"
            label="错误"
          >
            {{ executionDetail.record.errorInfo }}
          </ElDescriptionsItem>
        </ElDescriptions>

        <div v-if="detailLoadError" class="workflow-chat__detail-load-error">
          <span>{{ detailLoadError }}</span>
          <ElButton text type="primary" @click="loadExecutionDetail">
            重试
          </ElButton>
        </div>

        <div class="workflow-chat__detail-heading">
          <span>执行步骤</span>
          <span v-if="executionSteps.length > 0">
            {{ executionSteps.length }}
          </span>
        </div>
        <ol v-if="executionSteps.length > 0" class="workflow-chat__steps">
          <li
            v-for="step in executionSteps"
            :key="step.key"
            class="workflow-chat__step"
            :class="`is-${step.status}`"
          >
            <span class="workflow-chat__step-dot" aria-hidden="true"></span>
            <button
              class="workflow-chat__step-summary"
              type="button"
              :aria-expanded="isExecutionStepExpanded(step.key)"
              @click="toggleExecutionStep(step.key)"
            >
              <span class="workflow-chat__step-title">
                {{ step.nodeName }}
              </span>
              <span class="workflow-chat__step-meta">
                {{ executionStepStatusText(step.status) }}
                <template v-if="executionStepDuration(step)">
                  · {{ executionStepDuration(step) }}
                </template>
              </span>
              <span
                class="workflow-chat__step-chevron"
                :class="{ 'is-expanded': isExecutionStepExpanded(step.key) }"
                aria-hidden="true"
              >
                ›
              </span>
            </button>

            <div
              v-if="isExecutionStepExpanded(step.key)"
              class="workflow-chat__step-detail"
            >
              <section v-if="step.hasInput">
                <h3>输入</h3>
                <pre>{{ formatExecutionValue(step.input) }}</pre>
              </section>
              <section v-if="step.traces.length > 0">
                <h3>执行过程</h3>
                <ul class="workflow-chat__step-traces">
                  <li
                    v-for="trace in step.traces"
                    :key="trace.id"
                    :class="`is-${trace.outcome}`"
                  >
                    {{ executionTraceText(trace) }}
                  </li>
                </ul>
              </section>
              <section v-if="step.hasOutput">
                <h3>输出</h3>
                <pre>{{ formatExecutionValue(step.output, true) }}</pre>
              </section>
              <section v-if="step.error">
                <h3>错误</h3>
                <p class="workflow-chat__detail-error">{{ step.error }}</p>
              </section>
              <p
                v-if="
                  !step.hasInput &&
                  !step.hasOutput &&
                  !step.error &&
                  step.traces.length === 0
                "
                class="workflow-chat__step-empty"
              >
                暂无详细信息
              </p>
            </div>
          </li>
        </ol>
        <ElEmpty
          v-else-if="!detailLoading"
          description="暂无运行步骤"
          :image-size="64"
        />
      </div>
    </ElDrawer>
  </div>
</template>

<style scoped>
.workflow-chat {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: calc(100vh - 88px);
  min-height: 560px;
  overflow: hidden;
  color: var(--el-text-color-primary);
  background: var(--el-bg-color-page);
}

.workflow-chat.is-share {
  height: 100vh;
  min-height: 100vh;
}

.workflow-chat__header {
  z-index: 2;
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: space-between;
  min-height: 72px;
  padding: 12px 24px;
  background: var(--el-bg-color);
  background: color-mix(in srgb, var(--el-bg-color) 94%, transparent);
  border-bottom: 1px solid var(--el-border-color-lighter);
  backdrop-filter: blur(12px);
}

.workflow-chat__identity,
.workflow-chat__title-row,
.workflow-chat__actions {
  display: flex;
  align-items: center;
}

.workflow-chat__identity {
  gap: 12px;
  min-width: 0;
}

.workflow-chat__heading {
  min-width: 0;
}

.workflow-chat__title-row {
  gap: 8px;
}

.workflow-chat__title-row h1,
.workflow-chat__form-heading h2 {
  margin: 0;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.workflow-chat__title-row h1 {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 16px;
  line-height: 24px;
  white-space: nowrap;
}

.workflow-chat__heading p,
.workflow-chat__form-heading p {
  margin: 2px 0 0;
  color: var(--el-text-color-secondary);
}

.workflow-chat__heading p {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  line-height: 18px;
  white-space: nowrap;
}

.workflow-chat__actions {
  gap: 4px;
}

.workflow-chat__main {
  position: relative;
  display: flex;
  flex: 1;
  flex-direction: column;
  min-height: 0;
}

.workflow-chat__state {
  width: min(720px, calc(100% - 48px));
  margin: auto;
}

.workflow-chat__timeline {
  width: min(920px, 100%);
  padding: 24px 24px 152px;
  margin: 0 auto;
}

.workflow-chat__timeline :deep(.chat-timeline-item__bubble.is-user) {
  padding: 10px 14px;
  background: var(--el-color-primary-light-9);
  border: 1px solid var(--el-color-primary-light-8);
  border-radius: 16px 16px 4px;
}

.workflow-chat__timeline :deep(.chat-timeline-item__bubble.is-assistant) {
  padding: 8px 0;
}

.workflow-chat__form-card {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 3;
  width: min(560px, calc(100% - 48px));
  max-height: calc(100% - 176px);
  padding: 24px;
  overflow: auto;
  background: var(--el-bg-color);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 16px;
  box-shadow: var(--el-box-shadow-light);
  transform: translate(-50%, -50%);
}

.workflow-chat__form-card.is-confirm {
  top: auto;
  bottom: 112px;
  max-height: calc(100% - 160px);
  transform: translateX(-50%);
}

.workflow-chat__form-heading {
  margin-bottom: 20px;
}

.workflow-chat__form-heading h2 {
  font-size: 18px;
  line-height: 26px;
}

.workflow-chat__form-heading p {
  font-size: 13px;
  line-height: 20px;
}

.workflow-chat__confirm-error {
  padding: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 0;
  font-size: 12px;
  line-height: 20px;
  color: var(--el-color-danger);
  background: var(--el-color-danger-light-9);
  border-radius: var(--radius-control);
}

.workflow-chat__form-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 8px;
}

.workflow-chat__composer {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  align-items: center;
  padding: var(--space-5) var(--space-6);
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent,
    var(--el-bg-color-page) 32%
  );
}

.workflow-chat__input-shell {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  width: min(920px, 100%);
  pointer-events: auto;
}

.workflow-chat__parameter-panel,
.workflow-chat__input-row {
  background: var(--el-bg-color);
  background: color-mix(in srgb, var(--el-bg-color) 96%, transparent);
  border: 1px solid var(--el-border-color-lighter);
  backdrop-filter: blur(18px);
  transition:
    border-color var(--motion-duration-base) var(--motion-ease-standard),
    box-shadow var(--motion-duration-base) var(--motion-ease-standard);
}

.workflow-chat__parameter-panel {
  overflow: hidden;
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-subtle);
}

.workflow-chat__input-row {
  overflow: hidden;
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-toolbar);
}

.workflow-chat__input-row:focus-within {
  border-color: var(--el-color-primary-light-5);
  box-shadow:
    0 0 0 3px var(--el-color-primary-light-9),
    var(--shadow-toolbar);
}

.workflow-chat__parameter-summary {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  min-height: 48px;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--el-border-color-extra-light);
}

.workflow-chat__parameter-panel:not(.is-expanded)
  .workflow-chat__parameter-summary {
  border-bottom-color: transparent;
}

.workflow-chat__parameter-heading {
  display: flex;
  flex: 0 0 auto;
  gap: var(--space-2);
  align-items: center;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.workflow-chat__parameter-chips {
  display: flex;
  flex: 1;
  gap: var(--space-2);
  min-width: 0;
  overflow: hidden;
}

.workflow-chat__parameter-chip {
  display: inline-flex;
  flex: 1 1 0;
  gap: var(--space-2);
  align-items: center;
  min-width: 0;
  max-width: 240px;
  min-height: 36px;
  padding: 0 var(--space-3);
  font: inherit;
  font-size: 12px;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  background: hsl(var(--surface-subtle));
  border: 1px solid var(--el-border-color-light);
  border-radius: var(--radius-control);
  transition:
    color var(--motion-duration-fast) var(--motion-ease-standard),
    background-color var(--motion-duration-fast) var(--motion-ease-standard),
    border-color var(--motion-duration-fast) var(--motion-ease-standard);
}

.workflow-chat__parameter-chip.is-overflow {
  flex: 0 0 auto;
  min-width: 42px;
  color: hsl(var(--primary));
}

.workflow-chat__parameter-chip:hover,
.workflow-chat__parameter-chip:focus-visible {
  color: hsl(var(--primary));
  outline: none;
  background: hsl(var(--primary) / 6%);
  border-color: hsl(var(--primary) / 28%);
}

.workflow-chat__parameter-chip:focus-visible {
  box-shadow: 0 0 0 3px hsl(var(--primary) / 10%);
}

.workflow-chat__parameter-chip.is-pending strong {
  color: var(--el-color-warning);
}

.workflow-chat__parameter-chip span,
.workflow-chat__parameter-chip strong {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.workflow-chat__parameter-chip span {
  flex: 0 1 auto;
}

.workflow-chat__parameter-chip strong {
  flex: 1 1 auto;
  font-weight: 500;
  color: var(--el-text-color-primary);
}

.workflow-chat__parameter-toggle {
  display: inline-flex;
  flex: 0 0 auto;
  gap: var(--space-1);
  align-items: center;
  min-height: 36px;
  padding: 0 var(--space-2);
  margin-left: auto;
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  color: hsl(var(--primary));
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.workflow-chat__parameter-toggle:hover,
.workflow-chat__parameter-toggle:focus-visible {
  outline: none;
  background: hsl(var(--primary) / 7%);
}

.workflow-chat__parameter-toggle:focus-visible {
  box-shadow: 0 0 0 3px hsl(var(--primary) / 10%);
}

.workflow-chat__parameter-editor {
  max-height: min(46vh, 420px);
  padding: var(--space-3) var(--space-4) var(--space-4);
  overflow: auto;
  overscroll-behavior: contain;
}

.workflow-chat__parameter-description {
  margin: 0 0 var(--space-3);
  font-size: 12px;
  line-height: 20px;
  color: var(--el-text-color-secondary);
}

.workflow-chat__parameter-form fieldset {
  min-width: 0;
  padding: 0;
  margin: 0;
  border: 0;
}

.workflow-chat__parameter-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3) var(--space-4);
}

.workflow-chat__parameter-grid :deep(.workflow-form-item) {
  min-width: 0;
  margin-bottom: 0;
}

.workflow-chat__parameter-grid :deep(.workflow-form-item.is-wide) {
  grid-column: 1 / -1;
}

.workflow-chat__parameter-grid :deep(.el-form-item__label) {
  padding-bottom: var(--space-2);
  font-size: 12px;
  font-weight: 500;
  line-height: 18px;
  color: var(--el-text-color-secondary);
}

.workflow-chat__parameter-grid :deep(.el-input__wrapper),
.workflow-chat__parameter-grid :deep(.el-select__wrapper),
.workflow-chat__parameter-grid :deep(.el-textarea__inner) {
  border-radius: var(--radius-control);
}

.workflow-chat__more-parameters {
  padding-top: var(--space-2);
  margin-top: var(--space-3);
  border-top: 1px solid var(--el-border-color-extra-light);
}

.workflow-chat__more-parameters-toggle {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  min-height: 36px;
  padding: 0 var(--space-2);
  font: inherit;
  font-size: 12px;
  font-weight: 500;
  color: var(--el-text-color-secondary);
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.workflow-chat__more-parameters-toggle small {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 var(--space-1);
  font-size: 11px;
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 8%);
  border-radius: 999px;
}

.workflow-chat__more-parameters-toggle:hover,
.workflow-chat__more-parameters-toggle:focus-visible {
  color: hsl(var(--primary));
  outline: none;
  background: hsl(var(--primary) / 6%);
}

.workflow-chat__more-parameters-toggle:focus-visible {
  box-shadow: 0 0 0 3px hsl(var(--primary) / 10%);
}

.workflow-chat__more-parameters-grid {
  margin-top: var(--space-3);
}

.workflow-chat__input-row {
  display: flex;
  gap: var(--space-3);
  align-items: flex-end;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
}

.workflow-chat__input-row textarea {
  flex: 1;
  min-height: 48px;
  max-height: 120px;
  padding: var(--space-2) 0;
  overflow: auto;
  font: inherit;
  line-height: 22px;
  color: var(--el-text-color-primary);
  resize: none;
  outline: 0;
  background: transparent;
  border: 0;
}

.workflow-chat__input-row textarea::placeholder {
  color: var(--el-text-color-placeholder);
}

.workflow-chat__input-row textarea:disabled {
  cursor: not-allowed;
}

.workflow-chat__send {
  flex: 0 0 auto;
  width: 40px;
  min-width: 40px;
  height: 40px;
}

.workflow-chat__send.is-stop::before {
  display: block;
  width: 12px;
  height: 12px;
  content: '';
  background: var(--el-color-white);
  border-radius: 2px;
}

.workflow-chat__composer > span {
  font-size: 11px;
  color: var(--el-text-color-placeholder);
}

.workflow-parameters-enter-active,
.workflow-parameters-leave-active {
  overflow: hidden;
  transition:
    max-height var(--motion-duration-medium) var(--motion-ease-decelerate),
    opacity var(--motion-duration-base) var(--motion-ease-standard),
    transform var(--motion-duration-medium) var(--motion-ease-decelerate);
}

.workflow-parameters-enter-from,
.workflow-parameters-leave-to {
  max-height: 0;
  opacity: 0;
  transform: translateY(8px);
}

.workflow-parameter-more-enter-active,
.workflow-parameter-more-leave-active {
  transition:
    opacity var(--motion-duration-base) var(--motion-ease-standard),
    transform var(--motion-duration-base) var(--motion-ease-standard);
}

.workflow-parameter-more-enter-from,
.workflow-parameter-more-leave-to {
  opacity: 0;
  transform: translateY(-4px);
}

.workflow-chat__detail {
  min-height: 240px;
}

.workflow-chat__detail-heading {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: var(--space-6) 0 var(--space-2);
  font-size: 14px;
  font-weight: 600;
}

.workflow-chat__detail-heading > span:last-child {
  font-size: 12px;
  font-weight: 400;
  color: var(--el-text-color-placeholder);
}

.workflow-chat__detail-id {
  font-family: var(--el-font-family-monospace, monospace);
  font-size: 12px;
  word-break: break-all;
}

.workflow-chat__detail-load-error {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
  font-size: 12px;
  color: var(--el-color-danger);
  background: var(--el-color-danger-light-9);
  border-radius: var(--radius-control);
}

.workflow-chat__detail-error {
  margin: 0;
  font-size: 12px;
  line-height: 20px;
  color: var(--el-color-danger);
  overflow-wrap: anywhere;
}

.workflow-chat__steps {
  padding: 0;
  margin: 0;
  list-style: none;
}

.workflow-chat__step {
  position: relative;
  padding-left: var(--space-8);
}

.workflow-chat__step:not(:last-child)::before {
  position: absolute;
  top: 24px;
  bottom: -16px;
  left: 6px;
  width: 2px;
  content: '';
  background: var(--el-border-color-lighter);
}

.workflow-chat__step-dot {
  position: absolute;
  top: 18px;
  left: 0;
  z-index: 1;
  width: 14px;
  height: 14px;
  background: var(--el-border-color);
  border: 3px solid var(--el-bg-color);
  border-radius: var(--radius-pill);
}

.workflow-chat__step.is-running .workflow-chat__step-dot {
  background: hsl(var(--primary));
}

.workflow-chat__step.is-running .workflow-chat__step-dot::after {
  position: absolute;
  inset: -7px;
  content: '';
  background: hsl(var(--primary) / 22%);
  border-radius: inherit;
  animation: workflow-chat-node-pulse 1.6s var(--motion-ease-standard) infinite;
  will-change: opacity, transform;
}

.workflow-chat__step.is-waiting .workflow-chat__step-dot {
  background: var(--el-color-warning);
}

.workflow-chat__step.is-failed .workflow-chat__step-dot {
  background: var(--el-color-danger);
}

.workflow-chat__step-summary {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto 20px;
  gap: var(--space-2);
  align-items: center;
  width: 100%;
  min-height: 48px;
  padding: var(--space-2) 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
}

.workflow-chat__step-summary:hover .workflow-chat__step-title {
  color: hsl(var(--primary));
}

.workflow-chat__step-summary:focus-visible {
  outline: 2px solid hsl(var(--primary) / 45%);
  outline-offset: 2px;
}

.workflow-chat__step-title {
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 14px;
  font-weight: 600;
  line-height: 22px;
  white-space: nowrap;
  transition: color var(--motion-duration-base) var(--motion-ease-standard);
}

.workflow-chat__step-meta {
  font-size: 12px;
  line-height: 20px;
  color: var(--el-text-color-placeholder);
  white-space: nowrap;
}

.workflow-chat__step.is-running .workflow-chat__step-meta {
  color: hsl(var(--primary));
}

.workflow-chat__step.is-failed .workflow-chat__step-meta {
  color: var(--el-color-danger);
}

.workflow-chat__step-chevron {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 20px;
  color: var(--el-text-color-placeholder);
  transition: transform var(--motion-duration-base) var(--motion-ease-standard);
}

.workflow-chat__step-chevron.is-expanded {
  transform: rotate(90deg);
}

.workflow-chat__step-detail {
  display: grid;
  gap: var(--space-2);
  padding: 0 0 var(--space-4);
}

.workflow-chat__step-detail section {
  min-width: 0;
  padding: var(--space-3);
  background: hsl(var(--input-background));
  border: 1px solid hsl(var(--border));
  border-radius: var(--radius-control);
}

.workflow-chat__step-detail h3 {
  margin: 0 0 var(--space-2);
  font-size: 12px;
  font-weight: 600;
  line-height: 20px;
  color: var(--el-text-color-secondary);
}

.workflow-chat__step-detail pre {
  max-height: 320px;
  padding: 0;
  margin: 0;
  overflow: auto;
  font-family: var(--el-font-family-monospace, monospace);
  font-size: 12px;
  line-height: 20px;
  color: var(--el-text-color-primary);
  overflow-wrap: anywhere;
  white-space: pre-wrap;
}

.workflow-chat__step-traces {
  display: grid;
  gap: var(--space-1);
  padding: 0;
  margin: 0;
  font-size: 12px;
  line-height: 20px;
  list-style: none;
}

.workflow-chat__step-traces li::before {
  display: inline-block;
  width: 6px;
  height: 6px;
  margin-right: var(--space-2);
  vertical-align: 1px;
  content: '';
  background: var(--el-text-color-placeholder);
  border-radius: var(--radius-pill);
}

.workflow-chat__step-traces li.is-matched::before {
  background: hsl(var(--primary));
}

.workflow-chat__step-empty {
  margin: 0;
  font-size: 12px;
  line-height: 20px;
  color: var(--el-text-color-placeholder);
}

@keyframes workflow-chat-node-pulse {
  0% {
    opacity: 0.55;
    transform: scale(0.68);
  }

  70%,
  100% {
    opacity: 0;
    transform: scale(1.25);
  }
}

@media (max-width: 720px) {
  .workflow-chat__header {
    padding: 12px 16px;
  }

  .workflow-chat__heading p,
  .workflow-chat__title-row .el-tag {
    display: none;
  }

  .workflow-chat__timeline {
    padding: 16px 16px 168px;
  }

  .workflow-chat__composer {
    padding: 12px 16px;
  }

  .workflow-chat__parameter-summary {
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
  }

  .workflow-chat__parameter-heading {
    flex: 1;
  }

  .workflow-chat__parameter-chips {
    flex-basis: calc(100% - 72px);
    order: 3;
  }

  .workflow-chat__parameter-toggle {
    margin-left: auto;
  }

  .workflow-chat__parameter-editor {
    max-height: min(46vh, 440px);
    padding: var(--space-3);
  }

  .workflow-chat__parameter-grid {
    grid-template-columns: 1fr;
    gap: var(--space-3);
  }

  .workflow-chat__input-row {
    gap: var(--space-2);
    padding: var(--space-2) var(--space-2) var(--space-2) var(--space-3);
  }

  .workflow-chat__form-card {
    width: calc(100% - 32px);
    padding: 20px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .workflow-chat__step.is-running .workflow-chat__step-dot::after {
    animation: none;
  }

  .workflow-chat__step-chevron,
  .workflow-chat__step-title,
  .workflow-parameter-more-enter-active,
  .workflow-parameter-more-leave-active,
  .workflow-parameters-enter-active,
  .workflow-parameters-leave-active {
    transition: none;
  }
}
</style>
