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

import type {
  ActionButton,
  CardPrimaryAction,
} from '#/components/page/CardList.vue';
import type { OfflineImpactCheck } from '#/views/ai/shared/offline-impact';

import { computed, h, markRaw, onMounted, ref } from 'vue';

import ElXMarkdown from 'vue-element-plus-x/es/XMarkdown/index.js';

import { useAccess } from '@easyflow/access';
import { EasyFlowFormModal } from '@easyflow/common-ui';
import { useAppConfig } from '@easyflow/hooks';

import {
  Check,
  CopyDocument,
  Delete,
  Download,
  Edit,
  Link,
  Lock,
  OfficeBuilding,
  Plus,
  Promotion,
  Tickets,
  Upload,
  VideoPlay,
} from '@element-plus/icons-vue';
import {
  ElDialog,
  ElForm,
  ElFormItem,
  ElIcon,
  ElInput,
  ElInputNumber,
  ElMessage,
  ElMessageBox,
  ElPopover,
} from 'element-plus';
import { tryit } from 'radash';

import { api } from '#/api/request';
import workflowIcon from '#/assets/ai/workflow/workflowIcon.png';
// import workflowSvg from '#/assets/workflow.svg';
import HeaderSearch from '#/components/headerSearch/HeaderSearch.vue';
import DesignIcon from '#/components/icons/DesignIcon.vue';
import CardList from '#/components/page/CardList.vue';
import PageData from '#/components/page/PageData.vue';
import PageSide from '#/components/page/PageSide.vue';
import { $t } from '#/locales';
import { router } from '#/router';
import { useDictStore } from '#/store';
import AiResourceCornerMeta from '#/views/ai/shared/AiResourceCornerMeta.vue';
import { buildOfflineImpactMessage } from '#/views/ai/shared/offline-impact';
import {
  canAiResourceDelete,
  canAiResourceOffline,
  canAiResourcePublish,
  canAiResourceRepublish,
  isAiResourceApprovalPending,
  resolveAiResourceDisplayStatus,
} from '#/views/ai/shared/publish-status';

import WorkflowModal from './WorkflowModal.vue';

const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);

interface FieldDefinition {
  // 字段名称
  prop: string;
  // 字段标签
  label: string;
  // 字段类型：input, number, select, radio, checkbox, switch, date, datetime
  type?: 'input' | 'number';
  // 是否必填
  required?: boolean;
  // 占位符
  placeholder?: string;
}

type VisibilityScope = 'DEPT' | 'PRIVATE' | 'PUBLIC';

interface ApiFieldDoc {
  key: string;
  label: string;
  type: string;
  required: boolean;
  description: string;
  placeholder: string;
}

const primaryAction: CardPrimaryAction = {
  icon: DesignIcon,
  text: $t('button.design'),
  permission: '/api/v1/workflow/save',
  onClick: (row: any) => {
    toDesignPage(row);
  },
};

const { hasAccessByCodes } = useAccess();
const canManageWorkflow = computed(() =>
  hasAccessByCodes(['/api/v1/workflow/save']),
);
const updatingScopeId = ref<null | number | string>(null);
const visibilityScopePopoverRefs = ref<Record<string, any>>({});
const apiInstructionVisible = ref(false);
const apiInstructionRow = ref<any>(null);
const visibilityScopeMeta = computed(() => ({
  PRIVATE: {
    label: $t('aiWorkflow.visibilityScopePrivate'),
    description: $t('aiWorkflow.visibilityScopePrivateDesc'),
    icon: Lock,
    tone: 'private',
  },
  DEPT: {
    label: $t('aiWorkflow.visibilityScopeDept'),
    description: $t('aiWorkflow.visibilityScopeDeptDesc'),
    icon: OfficeBuilding,
    tone: 'dept',
  },
  PUBLIC: {
    label: $t('aiWorkflow.visibilityScopePublic'),
    description: $t('aiWorkflow.visibilityScopePublicDesc'),
    icon: Promotion,
    tone: 'public',
  },
}));
const visibilityScopeOptions = computed(() => {
  return (['PRIVATE', 'DEPT', 'PUBLIC'] as VisibilityScope[]).map((value) => ({
    value,
    ...visibilityScopeMeta.value[value],
  }));
});

const actions: ActionButton[] = [
  {
    icon: Edit,
    text: $t('button.edit'),
    permission: '/api/v1/workflow/save',
    placement: 'inline',
    onClick: (row: any) => {
      showDialog(row);
    },
  },
  {
    icon: VideoPlay,
    text: $t('button.run'),
    placement: 'inline',
    onClick: (row: any) => {
      router.push({
        name: 'RunPage',
        query: {
          id: row.id,
        },
      });
    },
  },
  {
    icon: Tickets,
    text: $t('aiWorkflowExecRecord.moduleName'),
    permission: '/api/v1/workflow/save',
    placement: 'menu',
    onClick: (row: any) => {
      router.push({
        name: 'ExecRecord',
        query: {
          workflowId: row.id,
        },
      });
    },
  },
  {
    icon: Link,
    text: $t('aiWorkflow.apiInstruction'),
    placement: 'menu',
    onClick: (row: any) => {
      showApiInstruction(row);
    },
  },
  {
    icon: Download,
    text: $t('button.export'),
    permission: '/api/v1/workflow/save',
    placement: 'menu',
    onClick: (row: any) => {
      exportJson(row);
    },
  },
  {
    icon: CopyDocument,
    text: $t('button.copy'),
    permission: '/api/v1/workflow/save',
    placement: 'menu',
    onClick: (row: any) => {
      showDialog({
        title: `${row.title}Copy`,
        content: row.content,
      });
    },
  },
  {
    icon: Promotion,
    text: (row: any) =>
      canAiResourceRepublish(row.displayPublishStatus, row.publishStatus)
        ? $t('button.republish')
        : $t('button.publish'),
    permission: '/api/v1/workflow/save',
    placement: 'inline',
    visible: (row: any) =>
      canAiResourcePublish(row.displayPublishStatus, row.publishStatus) ||
      canAiResourceRepublish(row.displayPublishStatus, row.publishStatus),
    onClick: (row: any) => {
      submitPublishAction(row);
    },
  },
  {
    icon: Promotion,
    text: $t('button.offline'),
    permission: '/api/v1/workflow/save',
    placement: 'menu',
    visible: (row: any) =>
      canAiResourceOffline(row.displayPublishStatus, row.publishStatus),
    onClick: (row: any) => {
      submitOfflineAction(row);
    },
  },
  {
    icon: Delete,
    text: $t('button.delete'),
    tone: 'danger',
    permission: '/api/v1/workflow/remove',
    placement: 'menu',
    visible: (row: any) =>
      canAiResourceDelete(row.displayPublishStatus, row.publishStatus),
    onClick: (row: any) => {
      submitDeleteApproval(row);
    },
  },
];
onMounted(() => {
  initDict();
  getSideList();
});
const pageDataRef = ref();
const saveDialog = ref();
const dictStore = useDictStore();
const headerButtons = [
  {
    key: 'create',
    text: $t('button.add'),
    icon: markRaw(Plus),
    type: 'primary',
    data: { action: 'create' },
    permission: '/api/v1/workflow/save',
  },
  {
    key: 'import',
    text: $t('button.import'),
    icon: markRaw(Upload),
    type: 'default',
    data: { action: 'import' },
    permission: '/api/v1/workflow/save',
  },
];

function initDict() {
  dictStore.fetchDictionary('dataStatus');
}
function resolveVisibilityScopeMeta(scope?: string) {
  return (
    visibilityScopeMeta.value[(scope || 'PRIVATE') as VisibilityScope] ||
    visibilityScopeMeta.value.PRIVATE
  );
}
function setVisibilityScopePopoverRef(id: number | string, el: any) {
  const cacheKey = String(id);
  if (el) {
    visibilityScopePopoverRefs.value[cacheKey] = el;
    return;
  }
  delete visibilityScopePopoverRefs.value[cacheKey];
}
function closeVisibilityScopePopover(id: number | string) {
  visibilityScopePopoverRefs.value[String(id)]?.hide?.();
}
async function updateVisibilityScope(
  row: any,
  visibilityScope: VisibilityScope,
) {
  if (
    !canManageWorkflow.value ||
    !row?.id ||
    updatingScopeId.value === row.id ||
    row.visibilityScope === visibilityScope
  ) {
    closeVisibilityScopePopover(row.id);
    return;
  }
  updatingScopeId.value = row.id;
  try {
    const res = await api.post('/api/v1/workflow/update', {
      id: row.id,
      visibilityScope,
    });
    if (res.errorCode === 0) {
      row.visibilityScope = visibilityScope;
      ElMessage.success(res.message);
      closeVisibilityScopePopover(row.id);
      pageDataRef.value?.reload?.();
    }
  } finally {
    updatingScopeId.value = null;
  }
}
const handleSearch = (params: string) => {
  pageDataRef.value.setQuery({ title: params, isQueryOr: true });
};
function reset() {
  pageDataRef.value.setQuery({});
}
function showDialog(row: any, importMode = false) {
  saveDialog.value.openDialog({ ...row }, importMode);
}
function resolveNavTitle(row: any) {
  return row?.title || row?.name || '';
}
function isRepublishAction(row: any) {
  return canAiResourceRepublish(row.displayPublishStatus, row.publishStatus);
}
function isWorkflowPublished(row: any) {
  return (
    resolveAiResourceDisplayStatus(
      row.displayPublishStatus,
      row.publishStatus,
    ) === 'PUBLISHED'
  );
}
function showApiInstruction(row: any) {
  if (!isWorkflowPublished(row) || !getPublishedWorkflowContent(row)) {
    ElMessage.warning($t('aiWorkflow.apiInstructionPublishRequired'));
    return;
  }
  apiInstructionRow.value = row;
  apiInstructionVisible.value = true;
}
function getPublishedWorkflowContent(row: any) {
  const snapshot = row?.publishedSnapshotJson;
  if (snapshot && typeof snapshot === 'object' && snapshot.content) {
    return snapshot.content;
  }
  return null;
}
function parseWorkflowContent(row: any) {
  const content = getPublishedWorkflowContent(row);
  if (!content) {
    return null;
  }
  if (typeof content === 'object') {
    return content;
  }
  try {
    return JSON.parse(String(content));
  } catch {
    return null;
  }
}
function normalizeApiOrigin(value?: string) {
  const rawValue = String(value || '').trim();
  if (!rawValue) {
    return window.location.origin;
  }
  if (/^https?:\/\//i.test(rawValue)) {
    return rawValue.replace(/\/+$/, '');
  }
  const normalizedPath = rawValue.startsWith('/') ? rawValue : `/${rawValue}`;
  return `${window.location.origin}${normalizedPath}`.replace(/\/+$/, '');
}
function resolveApiBaseUrl() {
  return `${normalizeApiOrigin(apiURL)}/public-api/workflow`;
}
function resolveStartNodeData(row: any) {
  const workflow = parseWorkflowContent(row);
  const nodes = Array.isArray(workflow?.nodes) ? workflow.nodes : [];
  const startNode = nodes.find((node: any) => node?.type === 'startNode');
  return startNode?.data || {};
}
function resolveApiFields(row: any): ApiFieldDoc[] {
  const startNodeData = resolveStartNodeData(row);
  const schema = Array.isArray(startNodeData.startFormSchema)
    ? startNodeData.startFormSchema
    : [];
  const parameters = Array.isArray(startNodeData.parameters)
    ? startNodeData.parameters
    : [];
  const source = schema.length > 0 ? schema : parameters;
  return source
    .map((item: any) => {
      const key = String(item?.key || item?.name || '').trim();
      if (!key) {
        return null;
      }
      return {
        key,
        label: String(item?.label || item?.title || item?.name || key),
        type: String(item?.type || item?.contentType || 'text'),
        required: Boolean(item?.required),
        description: String(item?.description || item?.formDescription || ''),
        placeholder: String(item?.placeholder || item?.formPlaceholder || ''),
      };
    })
    .filter(Boolean) as ApiFieldDoc[];
}
function buildExampleVariables(row: any) {
  const variables: Record<string, any> = {};
  for (const field of resolveApiFields(row)) {
    if (field.type === 'file') {
      variables[field.key] = [
        {
          fileName: 'example.pdf',
          filePath: 'https://example.com/example.pdf',
        },
      ];
      continue;
    }
    if (field.type === 'checkbox') {
      variables[field.key] = [];
      continue;
    }
    variables[field.key] = field.placeholder || field.label;
  }
  return variables;
}
function buildRunRequestExample(row: any) {
  return JSON.stringify(
    {
      id: row?.id,
      variables: buildExampleVariables(row),
    },
    null,
    2,
  );
}
function buildRunResponseExample() {
  return JSON.stringify(
    {
      errorCode: 0,
      message: '成功',
      data: '执行ID',
    },
    null,
    2,
  );
}
function buildResumeRequestExample() {
  return JSON.stringify(
    {
      executeId: '执行ID',
      confirmParams: {
        confirm: true,
      },
    },
    null,
    2,
  );
}
async function copyApiContent(content: string) {
  try {
    await navigator.clipboard.writeText(content);
    ElMessage.success($t('message.copySuccess'));
  } catch {
    ElMessage.error($t('message.copyFail'));
  }
}
function handleApiDocClick(e: MouseEvent) {
  const target = (e.target as HTMLElement).closest('.api-url-copy-btn');
  if (!target) return;
  const url = (target as HTMLElement).dataset.copy;
  if (url) copyApiContent(url);
}
function apiUrlLine(method: string, url: string) {
  const escaped = url.replace(/"/g, '&quot;');
  return `\`${method}\` \`${url}\` <button class="api-url-copy-btn" data-copy="${escaped}" title="复制">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg></button>`;
}

const apiDocMarkdown = computed(() => {
  const row = apiInstructionRow.value;
  if (!row) return '';

  const baseUrl = resolveApiBaseUrl();
  const fields = resolveApiFields(row);

  const lines: string[] = [];

  // ---- 概述 ----
  lines.push(`## 概述`);
  lines.push(``);
  lines.push(`通过 Public API 可异步执行已发布的工作流并获取执行结果。`);
  lines.push(``);
  lines.push(`**调用流程**：发起执行 → 轮询查询执行结果 → （若有确认节点）恢复执行`);
  lines.push(``);

  // ---- 鉴权 ----
  lines.push(`## 鉴权`);
  lines.push(``);
  lines.push(`所有请求需在 Header 中携带访问令牌，访问令牌需开启 **工作流 API 调用授权**。`);
  lines.push(``);
  lines.push('```');
  lines.push(`ApiKey: <your-api-key>`);
  lines.push('```');
  lines.push(``);
  lines.push(`---`);
  lines.push(``);

  // ---- 1. 发起执行 ----
  lines.push(`## 1. 发起执行`);
  lines.push(``);
  lines.push(apiUrlLine('POST', `${baseUrl}/runAsync`));
  lines.push(``);
  lines.push(`异步执行工作流，立即返回执行 ID。工作流必须已发布且存在发布快照。`);
  lines.push(``);
  lines.push(`### 请求体`);
  lines.push(``);
  lines.push('```json');
  lines.push(buildRunRequestExample(row));
  lines.push('```');
  lines.push(``);

  // 入参说明
  lines.push(`### 入参说明`);
  lines.push(``);
  lines.push(`| 参数 | 类型 | 必填 | 说明 |`);
  lines.push(`| --- | --- | --- | --- |`);
  lines.push(`| id | string | 是 | 工作流 ID |`);
  lines.push(`| variables | object | 否 | 运行参数，字段说明见下方 |`);
  lines.push(``);

  if (fields.length > 0) {
    lines.push(`#### variables 字段明细`);
    lines.push(``);
    lines.push(`以下字段基于当前发布快照的开始节点配置生成。`);
    lines.push(``);
    lines.push(`| 参数 | 类型 | 必填 | 说明 |`);
    lines.push(`| --- | --- | --- | --- |`);
    for (const f of fields) {
      const desc = [f.label, f.description].filter(Boolean).join(' · ');
      lines.push(`| ${f.key} | ${f.type} | ${f.required ? '是' : '否'} | ${desc} |`);
    }
    lines.push(``);
  }

  lines.push(`### 响应`);
  lines.push(``);
  lines.push('```json');
  lines.push(buildRunResponseExample());
  lines.push('```');
  lines.push(``);
  lines.push(`\`data\` 为 **执行 ID**（executeId），后续查询和恢复均需使用此 ID。`);
  lines.push(``);
  lines.push(`---`);
  lines.push(``);

  // ---- 2. 查询执行结果 ----
  lines.push(`## 2. 查询执行结果`);
  lines.push(``);
  lines.push(apiUrlLine('POST', `${baseUrl}/getChainStatus`));
  lines.push(``);
  lines.push(`查询工作流执行的整体状态与各节点执行详情。工作流为异步执行，建议**轮询**该接口直到状态为终态。`);
  lines.push(``);
  lines.push(`### 请求体`);
  lines.push(``);
  lines.push('```json');
  lines.push(JSON.stringify({ executeId: '<runAsync 返回的执行 ID>' }, null, 2));
  lines.push('```');
  lines.push(``);
  lines.push(`> \`nodes\` 参数可选。不传则只返回工作流整体状态；传入节点 ID 数组可额外获取对应节点的执行详情。`);
  lines.push(``);
  lines.push(`### 响应示例`);
  lines.push(``);
  lines.push('```json');
  lines.push(JSON.stringify({
    errorCode: 0,
    message: '成功',
    data: {
      executeId: 'abc5358c-a310-4caa-97ec-455062b2235e',
      status: 'FINISHED',
      message: null,
      result: { output: '工作流执行结果' },
      nodes: {},
    },
  }, null, 2));
  lines.push('```');
  lines.push(``);
  lines.push(`### 状态值说明`);
  lines.push(``);
  lines.push(`| 状态 | 说明 |`);
  lines.push(`| --- | --- |`);
  lines.push(`| READY | 就绪，尚未开始 |`);
  lines.push(`| RUNNING | 执行中 |`);
  lines.push(`| SUSPEND | 挂起，等待确认节点恢复 |`);
  lines.push(`| FINISHED | 执行完成 |`);
  lines.push(`| FAILED | 执行失败 |`);
  lines.push(`| ERROR | 执行异常 |`);
  lines.push(``);

  // ---- 3. 恢复执行 ----
  lines.push(`---`);
  lines.push(``);
  lines.push(`## 3. 恢复执行（确认节点）`);
  lines.push(``);
  lines.push(apiUrlLine('POST', `${baseUrl}/resume`));
  lines.push(``);
  lines.push(`当工作流包含**确认节点**时，执行到该节点后状态变为 \`SUSPEND\`，需要调用此接口传入确认参数后恢复执行。若工作流不包含确认节点则无需调用。`);
  lines.push(``);
  lines.push(`### 请求体`);
  lines.push(``);
  lines.push('```json');
  lines.push(buildResumeRequestExample());
  lines.push('```');
  lines.push(``);
  lines.push(`### 响应`);
  lines.push(``);
  lines.push('```json');
  lines.push(JSON.stringify({ errorCode: 0, message: '成功', data: null }, null, 2));
  lines.push('```');
  lines.push(``);
  lines.push(`恢复后可继续轮询 \`getChainStatus\` 获取后续执行状态。`);
  lines.push(``);

  // ---- 错误码 ----
  lines.push(`---`);
  lines.push(``);
  lines.push(`## 错误处理`);
  lines.push(``);
  lines.push(`当请求失败时，\`errorCode\` 不为 0，\`message\` 包含错误原因。常见错误：`);
  lines.push(``);
  lines.push(`| 场景 | 说明 |`);
  lines.push(`| --- | --- |`);
  lines.push(`| ApiKey 无效或过期 | 检查访问令牌状态与有效期 |`);
  lines.push(`| 未授权工作流 API 调用 | 在访问令牌中开启「工作流 API 调用授权」 |`);
  lines.push(`| 工作流尚未发布 | 仅已发布且存在发布快照的工作流可通过 API 调用 |`);

  return lines.join('\n');
});

async function submitPublishAction(row: any) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning($t('aiWorkflow.publishPendingHint'));
    return;
  }
  try {
    await ElMessageBox.confirm(
      isRepublishAction(row)
        ? $t('aiWorkflow.submitRepublishApprovalConfirm')
        : $t('aiWorkflow.submitPublishApprovalConfirm'),
      $t('message.noticeTitle'),
      {
        confirmButtonText: $t('button.confirm'),
        cancelButtonText: $t('button.cancel'),
        type: 'info',
      },
    );
  } catch {
    return;
  }
  const res = await api.post('/api/v1/workflow/submitPublishApproval', {
    id: row.id,
  });
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}
async function submitOfflineAction(row: any) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning($t('aiWorkflow.publishPendingHint'));
    return;
  }
  const impactRes = await api.get<{
    data: OfflineImpactCheck;
    errorCode: number;
  }>('/api/v1/workflow/offlineImpactCheck', {
    params: { id: row.id },
  });
  if (impactRes.errorCode !== 0) {
    return;
  }
  try {
    const sections = [];
    let offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundBotsFooter');
    if (impactRes.data?.hasBotBindings) {
      sections.push(
        buildOfflineImpactMessage(
          $t('aiWorkflow.offlineImpactBoundBotsIntro'),
          impactRes.data.botBindings,
          impactRes.data?.hasPluginBindings
            ? undefined
            : $t('aiWorkflow.offlineImpactBoundBotsFooter'),
        ),
      );
    }
    if (impactRes.data?.hasPluginBindings) {
      sections.push(
        buildOfflineImpactMessage(
          $t('aiWorkflow.offlineImpactBoundPluginsIntro'),
          impactRes.data.pluginBindings,
          impactRes.data?.hasBotBindings
            ? undefined
            : $t('aiWorkflow.offlineImpactBoundPluginsFooter'),
        ),
      );
    }
    if (impactRes.data?.hasBotBindings && impactRes.data?.hasPluginBindings) {
      offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundMixedFooter');
    } else if (impactRes.data?.hasPluginBindings) {
      offlineImpactFooter = $t('aiWorkflow.offlineImpactBoundPluginsFooter');
    }
    const impactMessage =
      sections.length > 0
        ? h('div', [
            ...sections,
            h('p', { style: 'margin-top: 12px;' }, offlineImpactFooter),
          ])
        : $t('aiWorkflow.submitOfflineApprovalConfirm');
    await ElMessageBox.confirm(impactMessage, $t('message.noticeTitle'), {
      confirmButtonText: $t('button.confirm'),
      cancelButtonText: $t('button.cancel'),
      type: 'warning',
    });
  } catch {
    return;
  }
  const res = await api.post('/api/v1/workflow/submitOfflineApproval', {
    id: row.id,
  });
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}
async function submitDeleteApproval(row: any) {
  if (
    isAiResourceApprovalPending(row.displayPublishStatus, row.publishStatus)
  ) {
    ElMessage.warning($t('aiWorkflow.deletePendingHint'));
    return;
  }
  try {
    await ElMessageBox.confirm(
      $t('aiWorkflow.submitDeleteApprovalConfirm'),
      $t('message.noticeTitle'),
      {
        confirmButtonText: $t('button.confirm'),
        cancelButtonText: $t('button.cancel'),
        type: 'warning',
      },
    );
  } catch {
    return;
  }
  const res = await api.post('/api/v1/workflow/submitDeleteApproval', {
    id: row.id,
  });
  if (res.errorCode === 0) {
    ElMessage.success(res.message || $t('message.saveOkMessage'));
    pageDataRef.value?.reload?.();
  }
}
function resolvePublishStatusMetaByInstance(
  displayPublishStatus?: string,
  publishStatus?: string,
) {
  switch (resolveAiResourceDisplayStatus(displayPublishStatus, publishStatus)) {
    case 'DELETE_PENDING': {
      return {
        label: $t('aiWorkflow.publishStatusDeletePending'),
        tone: 'danger',
      };
    }
    case 'OFFLINE': {
      return {
        label: $t('aiWorkflow.publishStatusOffline'),
        tone: 'draft',
      };
    }
    case 'OFFLINE_PENDING': {
      return {
        label: $t('aiWorkflow.publishStatusOfflinePending'),
        tone: 'pending',
      };
    }
    case 'PUBLISH_PENDING': {
      return {
        label: $t('aiWorkflow.publishStatusPublishPending'),
        tone: 'pending',
      };
    }
    case 'PUBLISHED': {
      return {
        label: $t('aiWorkflow.publishStatusPublished'),
        tone: 'published',
      };
    }
    default: {
      return {
        label: $t('aiWorkflow.publishStatusDraft'),
        tone: 'draft',
      };
    }
  }
}
function toDesignPage(row: any) {
  router.push({
    name: 'WorkflowDesign',
    query: {
      id: row.id,
      pageKey: '/ai/workflow',
      navTitle: resolveNavTitle(row),
    },
  });
}
function exportJson(row: any) {
  api
    .get('/api/v1/workflow/exportWorkFlow', {
      params: {
        id: row.id,
      },
    })
    .then((res) => {
      const text = res.data;
      const element = document.createElement('a');
      element.setAttribute(
        'href',
        `data:text/plain;charset=utf-8,${encodeURIComponent(text)}`,
      );
      element.setAttribute('download', `${row.title}.json`);
      element.style.display = 'none';
      document.body.append(element);
      element.click();
      element.remove();
      ElMessage.success($t('message.downloadSuccess'));
    });
}
const fieldDefinitions = ref<FieldDefinition[]>([
  {
    prop: 'categoryName',
    label: $t('aiWorkflowCategory.categoryName'),
    type: 'input',
    required: true,
    placeholder: $t('aiWorkflowCategory.categoryName'),
  },
  {
    prop: 'sortNo',
    label: $t('aiWorkflowCategory.sortNo'),
    type: 'number',
    required: false,
    placeholder: $t('aiWorkflowCategory.sortNo'),
  },
]);

const formData = ref<any>({});
const dialogVisible = ref(false);
const formRef = ref<FormInstance>();
const saveLoading = ref(false);
const sideList = ref<any[]>([]);
const controlBtns = [
  {
    icon: Edit,
    label: $t('button.edit'),
    onClick(row: any) {
      showControlDialog(row);
    },
  },
  {
    type: 'danger',
    icon: Delete,
    label: $t('button.delete'),
    onClick(row: any) {
      removeCategory(row);
    },
  },
];
const footerButton = {
  icon: Plus,
  label: $t('button.add'),
  onClick() {
    showControlDialog({});
  },
};

const formRules = computed(() => {
  const rules: Record<string, any[]> = {};
  fieldDefinitions.value.forEach((field) => {
    const fieldRules = [];
    if (field.required) {
      fieldRules.push({
        required: true,
        message: `${$t('message.required')}`,
        trigger: 'blur',
      });
    }
    if (fieldRules.length > 0) {
      rules[field.prop] = fieldRules;
    }
  });
  return rules;
});

function changeCategory(category: any) {
  pageDataRef.value.setQuery({ categoryId: category.id });
}
function showControlDialog(item: any) {
  formRef.value?.resetFields();
  formData.value = { ...item };
  dialogVisible.value = true;
}
function removeCategory(row: any) {
  ElMessageBox.confirm($t('message.deleteAlert'), $t('message.noticeTitle'), {
    confirmButtonText: $t('message.ok'),
    cancelButtonText: $t('message.cancel'),
    type: 'warning',
    beforeClose: (action, instance, done) => {
      if (action === 'confirm') {
        instance.confirmButtonLoading = true;
        api
          .post('/api/v1/workflowCategory/remove', { id: row.id })
          .then((res) => {
            instance.confirmButtonLoading = false;
            if (res.errorCode === 0) {
              ElMessage.success(res.message);
              done();
              getSideList();
            }
          })
          .catch(() => {
            instance.confirmButtonLoading = false;
          });
      } else {
        done();
      }
    },
  }).catch(() => {});
}
function handleSubmit() {
  formRef.value?.validate((valid) => {
    if (valid) {
      saveLoading.value = true;
      const url = formData.value.id
        ? '/api/v1/workflowCategory/update'
        : '/api/v1/workflowCategory/save';
      api.post(url, formData.value).then((res) => {
        saveLoading.value = false;
        if (res.errorCode === 0) {
          ElMessage.success(res.message);
          dialogVisible.value = false;
          getSideList();
        }
      });
    }
  });
}
const getSideList = async () => {
  const [, res] = await tryit(api.get)('/api/v1/workflowCategory/visibleList', {
    params: { sortKey: 'sortNo', sortType: 'asc' },
  });

  if (res && res.errorCode === 0) {
    sideList.value = [
      {
        id: '',
        categoryName: $t('common.allCategories'),
      },
      ...res.data,
    ];
  }
};
function handleHeaderButtonClick(data: any) {
  if (data.data.action === 'import') {
    showDialog({}, true);
  } else {
    showDialog({});
  }
}
</script>

<template>
  <div class="flex h-full flex-col gap-6 p-6">
    <WorkflowModal ref="saveDialog" @reload="reset" />
    <ElDialog
      v-model="apiInstructionVisible"
      :title="$t('aiWorkflow.apiInstruction')"
      width="780px"
      class="workflow-api-dialog"
      :footer="false"
    >
      <div
        v-if="apiInstructionRow"
        class="workflow-api-markdown-wrap"
        @click="handleApiDocClick"
      >
        <ElXMarkdown
          :markdown="apiDocMarkdown"
          :allow-html="true"
          :sanitize="false"
        />
      </div>
    </ElDialog>
    <HeaderSearch
      :buttons="headerButtons"
      @search="handleSearch"
      @button-click="handleHeaderButtonClick"
    />
    <div class="flex max-h-[calc(100vh-191px)] flex-1 gap-6">
      <PageSide
        label-key="categoryName"
        value-key="id"
        :menus="sideList"
        :control-btns="controlBtns"
        :footer-button="footerButton"
        @change="changeCategory"
      />
      <div class="h-full flex-1 overflow-auto">
        <PageData
          ref="pageDataRef"
          page-url="/api/v1/workflow/page"
          :page-sizes="[12, 18, 24]"
          :page-size="12"
        >
          <template #default="{ pageList }">
            <CardList
              :default-icon="workflowIcon"
              :data="pageList"
              :primary-action="primaryAction"
              :actions="actions"
            >
              <template #corner="{ item }">
                <AiResourceCornerMeta>
                  <template #publish>
                    <div
                      class="workflow-publish-chip"
                      :class="`workflow-publish-chip--${resolvePublishStatusMetaByInstance(item.displayPublishStatus, item.publishStatus).tone}`"
                    >
                      <span class="workflow-publish-chip__dot"></span>
                      <span>{{
                        resolvePublishStatusMetaByInstance(
                          item.displayPublishStatus,
                          item.publishStatus,
                        ).label
                      }}</span>
                    </div>
                  </template>
                  <template #scope>
                    <ElPopover
                      v-if="canManageWorkflow"
                      :ref="(el) => setVisibilityScopePopoverRef(item.id, el)"
                      trigger="click"
                      placement="bottom-end"
                      :width="208"
                      popper-class="workflow-visibility-popover"
                    >
                      <template #reference>
                        <button
                          type="button"
                          class="workflow-scope-chip"
                          :class="`workflow-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
                          :disabled="updatingScopeId === item.id"
                          @click.stop
                        >
                          <ElIcon class="workflow-scope-chip__icon">
                            <component
                              :is="
                                resolveVisibilityScopeMeta(item.visibilityScope)
                                  .icon
                              "
                            />
                          </ElIcon>
                          <span class="workflow-scope-chip__label">
                            {{
                              resolveVisibilityScopeMeta(item.visibilityScope)
                                .label
                            }}
                          </span>
                        </button>
                      </template>
                      <div class="workflow-scope-panel" @click.stop>
                        <button
                          v-for="option in visibilityScopeOptions"
                          :key="option.value"
                          type="button"
                          class="workflow-scope-option"
                          :class="[
                            `workflow-scope-option--${option.tone}`,
                            {
                              'workflow-scope-option--active':
                                item.visibilityScope === option.value,
                            },
                          ]"
                          :disabled="updatingScopeId === item.id"
                          @click.stop="
                            updateVisibilityScope(item, option.value)
                          "
                        >
                          <span class="workflow-scope-option__leading">
                            <span class="workflow-scope-option__icon-wrap">
                              <ElIcon class="workflow-scope-option__icon">
                                <component :is="option.icon" />
                              </ElIcon>
                            </span>
                            <span class="workflow-scope-option__text">
                              <span class="workflow-scope-option__label">
                                {{ option.label }}
                              </span>
                              <span class="workflow-scope-option__desc">
                                {{ option.description }}
                              </span>
                            </span>
                          </span>
                          <ElIcon
                            v-if="item.visibilityScope === option.value"
                            class="workflow-scope-option__check"
                          >
                            <Check />
                          </ElIcon>
                        </button>
                      </div>
                    </ElPopover>
                    <div
                      v-else
                      class="workflow-scope-chip workflow-scope-chip--readonly"
                      :class="`workflow-scope-chip--${resolveVisibilityScopeMeta(item.visibilityScope).tone}`"
                    >
                      <ElIcon class="workflow-scope-chip__icon">
                        <component
                          :is="
                            resolveVisibilityScopeMeta(item.visibilityScope)
                              .icon
                          "
                        />
                      </ElIcon>
                      <span class="workflow-scope-chip__label">
                        {{
                          resolveVisibilityScopeMeta(item.visibilityScope).label
                        }}
                      </span>
                    </div>
                  </template>
                </AiResourceCornerMeta>
              </template>
            </CardList>
          </template>
        </PageData>
      </div>
    </div>

    <EasyFlowFormModal
      v-model:open="dialogVisible"
      :closable="!saveLoading"
      :title="formData.id ? `${$t('button.edit')}` : `${$t('button.add')}`"
      :confirm-loading="saveLoading"
      :confirm-text="$t('button.confirm')"
      :submitting="saveLoading"
      @confirm="handleSubmit"
    >
      <ElForm
        ref="formRef"
        :model="formData"
        :rules="formRules"
        label-position="top"
        class="easyflow-modal-form easyflow-modal-form--compact"
      >
        <!-- 动态生成表单项 -->
        <ElFormItem
          v-for="field in fieldDefinitions"
          :key="field.prop"
          :label="field.label"
          :prop="field.prop"
        >
          <ElInput
            v-if="!field.type || field.type === 'input'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder"
          />
          <ElInputNumber
            v-else-if="field.type === 'number'"
            v-model="formData[field.prop]"
            :placeholder="field.placeholder"
            style="width: 100%"
          />
        </ElFormItem>
      </ElForm>
    </EasyFlowFormModal>
  </div>
</template>

<style scoped>
.workflow-publish-chip {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  min-width: 70px;
  padding: 0 9px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  border: 1px solid transparent;
  border-radius: 999px;
  box-shadow: inset 0 1px 0 hsl(var(--card) / 46%);
}

.workflow-publish-chip__dot {
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 999px;
  opacity: 0.88;
}

.workflow-publish-chip--draft {
  color: hsl(var(--muted-foreground));
  background: hsl(var(--muted) / 42%);
  border-color: hsl(var(--line-subtle));
}

.workflow-publish-chip--pending {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
  border-color: hsl(var(--warning) / 14%);
}

.workflow-publish-chip--published {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
  border-color: hsl(var(--success) / 14%);
}

.workflow-publish-chip--danger {
  color: hsl(var(--destructive));
  background: hsl(var(--destructive) / 10%);
  border-color: hsl(var(--destructive) / 14%);
}

.workflow-scope-chip {
  display: inline-flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  min-height: 22px;
  min-width: 70px;
  padding: 0 9px;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  color: hsl(var(--text-strong));
  background: transparent;
  border: 1px solid transparent;
  border-radius: 999px;
  transition:
    border-color 0.18s ease,
    background-color 0.18s ease,
    color 0.18s ease,
    transform 0.18s ease,
    box-shadow 0.18s ease;
}

button.workflow-scope-chip {
  cursor: pointer;
}

button.workflow-scope-chip:hover {
  background: hsl(var(--card) / 76%);
  box-shadow: 0 8px 16px -16px hsl(var(--foreground) / 28%);
}

button.workflow-scope-chip:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 4px hsl(var(--primary) / 12%),
    0 10px 22px -18px hsl(var(--foreground) / 32%);
}

button.workflow-scope-chip:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.workflow-scope-chip--readonly {
  cursor: default;
}

.workflow-scope-chip__icon {
  font-size: 12px;
}

.workflow-scope-chip__label {
  white-space: nowrap;
}

.workflow-scope-chip--private {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 10%);
  border-color: hsl(var(--primary) / 14%);
}

.workflow-scope-chip--dept {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
  border-color: hsl(var(--warning) / 14%);
}

.workflow-scope-chip--public {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
  border-color: hsl(var(--success) / 14%);
}

.workflow-scope-panel {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.workflow-scope-option {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 10px 8px;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: 12px;
  transition:
    background-color 0.18s ease,
    transform 0.18s ease,
    color 0.18s ease;
}

.workflow-scope-option:hover {
  background: hsl(var(--foreground) / 4%);
}

.workflow-scope-option:focus-visible {
  outline: none;
  box-shadow: 0 0 0 4px hsl(var(--primary) / 12%);
}

.workflow-scope-option:disabled {
  cursor: not-allowed;
  opacity: 0.72;
}

.workflow-scope-option__leading {
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 0;
}

.workflow-scope-option__icon-wrap {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: transparent;
  border-radius: 9px;
}

.workflow-scope-option__icon {
  font-size: 15px;
}

.workflow-scope-option__text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.workflow-scope-option__label {
  font-size: 13px;
  font-weight: 600;
  color: hsl(var(--text-strong));
}

.workflow-scope-option__desc {
  font-size: 11px;
  line-height: 1.35;
  color: hsl(var(--text-muted));
}

.workflow-scope-option__check {
  flex-shrink: 0;
  font-size: 15px;
}

.workflow-scope-option--private .workflow-scope-option__icon-wrap {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 10%);
}

.workflow-scope-option--dept .workflow-scope-option__icon-wrap {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
}

.workflow-scope-option--public .workflow-scope-option__icon-wrap {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
}

.workflow-scope-option--private.workflow-scope-option--active {
  background: hsl(var(--primary) / 8%);
}

.workflow-scope-option--dept.workflow-scope-option--active {
  background: hsl(var(--warning) / 8%);
}

.workflow-scope-option--public.workflow-scope-option--active {
  background: hsl(var(--success) / 8%);
}

.workflow-scope-option--private .workflow-scope-option__check {
  color: hsl(var(--primary));
}

.workflow-scope-option--dept .workflow-scope-option__check {
  color: hsl(var(--warning));
}

.workflow-scope-option--public .workflow-scope-option__check {
  color: hsl(var(--success));
}

:global(.workflow-visibility-popover.el-popover.el-popper) {
  padding: 8px;
  border-color: hsl(var(--line-subtle));
  border-radius: 16px;
  box-shadow: 0 18px 34px -28px hsl(var(--foreground) / 20%);
}

.workflow-api-markdown-wrap {
  max-height: 65vh;
  padding: 0 4px;
  overflow-y: auto;
  font-size: 14px;
  line-height: 1.7;
  color: hsl(var(--foreground));
}

.workflow-api-markdown-wrap::-webkit-scrollbar {
  width: 6px;
}

.workflow-api-markdown-wrap::-webkit-scrollbar-thumb {
  background-color: hsl(var(--muted-foreground) / 20%);
  border-radius: 3px;
}

.workflow-api-markdown-wrap :deep(h2) {
  padding-bottom: 8px;
  margin-top: 28px;
  margin-bottom: 16px;
  font-size: 18px;
  font-weight: 700;
  color: hsl(var(--foreground));
  border-bottom: 1px solid hsl(var(--border));
}

.workflow-api-markdown-wrap :deep(h2:first-child) {
  margin-top: 0;
}

.workflow-api-markdown-wrap :deep(h3) {
  margin-top: 20px;
  margin-bottom: 10px;
  font-size: 15px;
  font-weight: 600;
  color: hsl(var(--foreground));
}

.workflow-api-markdown-wrap :deep(h4) {
  margin-top: 16px;
  margin-bottom: 8px;
  font-size: 14px;
  font-weight: 600;
  color: hsl(var(--foreground));
}

.workflow-api-markdown-wrap :deep(hr) {
  margin: 24px 0;
  border: none;
  border-top: 1px solid hsl(var(--border));
}

.workflow-api-markdown-wrap :deep(p) {
  margin: 8px 0;
}

.workflow-api-markdown-wrap :deep(code) {
  padding: 2px 6px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 13px;
  color: hsl(var(--foreground));
  background: hsl(var(--muted) / 50%);
  border-radius: 4px;
}

.workflow-api-markdown-wrap :deep(pre) {
  max-height: 280px;
  padding: 14px 16px;
  margin: 10px 0;
  overflow: auto;
  background: hsl(220 14% 96%);
  border: 1px solid hsl(var(--border));
  border-radius: 8px;
}

.workflow-api-markdown-wrap :deep(pre code) {
  padding: 0;
  font-size: 12.5px;
  color: hsl(var(--foreground));
  background: transparent;
  border-radius: 0;
}

.workflow-api-markdown-wrap :deep(table) {
  width: 100%;
  margin: 10px 0;
  border-collapse: collapse;
}

.workflow-api-markdown-wrap :deep(th),
.workflow-api-markdown-wrap :deep(td) {
  padding: 8px 12px;
  font-size: 13px;
  text-align: left;
  border: 1px solid hsl(var(--border));
}

.workflow-api-markdown-wrap :deep(th) {
  font-weight: 600;
  color: hsl(var(--foreground));
  background: hsl(var(--muted) / 40%);
}

.workflow-api-markdown-wrap :deep(td) {
  color: hsl(var(--foreground) / 85%);
}

.workflow-api-markdown-wrap :deep(blockquote) {
  padding: 8px 16px;
  margin: 10px 0;
  color: hsl(var(--muted-foreground));
  border-left: 3px solid hsl(var(--primary) / 40%);
}

.workflow-api-markdown-wrap :deep(strong) {
  font-weight: 600;
  color: hsl(var(--foreground));
}

.workflow-api-markdown-wrap :deep(.api-url-copy-btn) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  margin-left: 6px;
  color: hsl(var(--muted-foreground));
  vertical-align: middle;
  cursor: pointer;
  background: transparent;
  border: 1px solid hsl(var(--border));
  border-radius: 6px;
  transition: all 0.15s;
}

.workflow-api-markdown-wrap :deep(.api-url-copy-btn:hover) {
  color: hsl(var(--primary));
  background: hsl(var(--muted) / 50%);
  border-color: hsl(var(--primary) / 40%);
}

:global(.workflow-api-dialog .el-dialog__body) {
  padding: 0 20px 20px;
  overflow: hidden;
}
</style>
