<script setup lang="ts">
import type {
  BatchModelInvokePublishPayload,
  llmType,
  ModelInvokeConfigPayload,
} from '#/api/ai/llm';

import { computed, onMounted, reactive, ref } from 'vue';

import { useAppConfig } from '@easyflow/hooks';

import {
  ElAlert,
  ElButton,
  ElCheckbox,
  ElEmpty,
  ElForm,
  ElFormItem,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElSwitch,
  ElTag,
} from 'element-plus';

import {
  batchUpdateInvokePublishStatus,
  getInvokeModelList,
  updateModelInvokeConfig,
} from '#/api/ai/llm';
import { api } from '#/api/request';
import ModelProviderBadge from '#/views/ai/model/ModelProviderBadge.vue';

const emit = defineEmits<{
  (e: 'updated'): void;
}>();

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

interface DraftState {
  id: string;
  invokeCode: string;
  publishEnabled: boolean;
}

interface FilterState {
  keyword: string;
}

const isLoading = ref(false);
const isSaving = ref(false);
const isPublishing = ref(false);
const isBatchPublishing = ref(false);
const lastErrorMessage = ref('');
const selectedModelId = ref('');
const autoGeneratedInvokeCode = ref('');
const publishBaseUrl = ref('');
const checkedModelIds = ref<string[]>([]);
const modelRows = ref<llmType[]>([]);

const filterState = reactive<FilterState>({
  keyword: '',
});

const draft = reactive<DraftState>({
  id: '',
  invokeCode: '',
  publishEnabled: false,
});

const normalizePublishBaseUrl = (value?: string) => {
  const raw = String(value || '').trim();
  if (!raw) {
    return '';
  }
  if (/^https?:\/\//i.test(raw)) {
    return raw.replace(/\/+$/, '');
  }
  if (raw.startsWith('//')) {
    return `${window.location.protocol}${raw}`.replace(/\/+$/, '');
  }
  const firstSegment = raw.split('/')[0] || '';
  const hasPort = /:\d+$/.test(firstSegment);
  let hostAndPath = raw;
  if (
    !hasPort &&
    firstSegment === window.location.hostname &&
    window.location.port
  ) {
    hostAndPath = `${firstSegment}:${window.location.port}${raw.slice(firstSegment.length)}`;
  }
  return `${window.location.protocol}//${hostAndPath}`.replace(/\/+$/, '');
};

const resolveFlowBaseUrl = (value?: string) => {
  const normalized = normalizePublishBaseUrl(value);
  if (!normalized) {
    return '';
  }

  if (/\/flow$/i.test(normalized)) {
    return normalized;
  }

  return `${normalized}/flow`;
};

const apiEndpoint = computed(() => {
  const base =
    resolveFlowBaseUrl(publishBaseUrl.value) ||
    resolveFlowBaseUrl(apiURL) ||
    resolveFlowBaseUrl(window.location.origin);
  return `${base}/v1/chat/completions`;
});

const selectedModel = computed(() =>
  modelRows.value.find((item) => String(item.id) === selectedModelId.value),
);

const visibleModelIds = computed(() =>
  filteredModels.value.map((item) => String(item.id)),
);

const checkedCount = computed(() => checkedModelIds.value.length);
const checkedModels = computed(() =>
  modelRows.value.filter((item) =>
    checkedModelIds.value.includes(String(item.id)),
  ),
);

const shouldBatchUnpublish = computed(
  () =>
    checkedModels.value.length > 0 &&
    checkedModels.value.every((item) => Boolean(item.publishEnabled)),
);

const batchActionLabel = computed(() =>
  shouldBatchUnpublish.value ? '取消发布' : '发布',
);

const isAllVisibleChecked = computed(
  () =>
    visibleModelIds.value.length > 0 &&
    visibleModelIds.value.every((id) => checkedModelIds.value.includes(id)),
);

const isPartiallyVisibleChecked = computed(
  () =>
    checkedModelIds.value.length > 0 &&
    visibleModelIds.value.some((id) => checkedModelIds.value.includes(id)) &&
    !isAllVisibleChecked.value,
);

const filteredModels = computed(() => {
  const keyword = filterState.keyword.trim().toLowerCase();

  return modelRows.value.filter((item) => {
    if (!keyword) {
      return true;
    }

    const searchTargets = [
      item.title,
      item.modelName,
      item.invokeCode,
      item.modelProvider?.providerName,
    ]
      .filter(Boolean)
      .map((text) => String(text).toLowerCase());

    return searchTargets.some((text) => text.includes(keyword));
  });
});

const providerType = computed(
  () =>
    selectedModel.value?.modelProvider?.providerType ||
    selectedModel.value?.aiLlmProvider?.providerType ||
    '-',
);

const upstreamModelName = computed(
  () => selectedModel.value?.modelName || selectedModel.value?.llmModel || '-',
);

const capabilityTags = computed(() => {
  const tags = ['文本', '流式'];
  if (selectedModel.value?.supportImage) {
    tags.push(
      selectedModel.value?.supportImageB64Only
        ? '图片输入（Base64）'
        : '图片输入',
    );
  }
  if (selectedModel.value?.supportTool) {
    tags.push('tools');
  }
  if (selectedModel.value?.supportToolMessage) {
    tags.push('tool 消息');
  }
  return tags;
});

const authExample = computed(() => 'Authorization: Bearer <your-access-token>');

const statusHints = computed(() => {
  const hints: string[] = [];
  if (!draft.invokeCode.trim()) {
    hints.push(
      '当前未单独配置对外模型标识（invokeCode），保存时会默认按上游模型 ID 生成。',
    );
  }
  return hints;
});

const curlExample = computed(
  () => `curl ${apiEndpoint.value} \\
  -H "Content-Type: application/json" \\
  -H "${authExample.value}" \\
  -d '{
    "model": "${draft.invokeCode.trim() || 'your-model-code'}",
    "messages": [
      {
        "role": "user",
        "content": "你好，介绍一下你自己。"
      }
    ]
  }'`,
);

const jsExample = computed(
  () => `await fetch("${apiEndpoint.value}", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": "Bearer <your-access-token>"
  },
  body: JSON.stringify({
    model: "${draft.invokeCode.trim() || 'your-model-code'}",
    messages: [
      { role: "user", content: "你好，介绍一下你自己。" }
    ]
  })
});`,
);

const pythonExample = computed(
  () => `import requests

resp = requests.post(
    "${apiEndpoint.value}",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer <your-access-token>",
    },
    json={
        "model": "${draft.invokeCode.trim() || 'your-model-code'}",
        "messages": [
            {"role": "user", "content": "你好，介绍一下你自己。"}
        ],
    },
    timeout=60,
)

print(resp.json())`,
);

const buildDefaultInvokeCode = (modelName?: string) => {
  const normalized = String(modelName || '')
    .trim()
    .replaceAll(/[^\w.:-]+/g, '-')
    .replaceAll(/-+/g, '-')
    .replaceAll(/^[^\da-z]+/gi, '')
    .slice(0, 128);

  if (!normalized) {
    return '';
  }

  return normalized.length === 1 ? `${normalized}-model` : normalized;
};

const isInvokeCodeDirty = computed(() => {
  if (!selectedModel.value) {
    return false;
  }

  return (
    draft.invokeCode.trim() !==
    (selectedModel.value.invokeCode || autoGeneratedInvokeCode.value)
  );
});

const syncDraft = (model?: llmType | null) => {
  if (!model) {
    selectedModelId.value = '';
    draft.id = '';
    draft.invokeCode = '';
    draft.publishEnabled = false;
    autoGeneratedInvokeCode.value = '';
    return;
  }

  autoGeneratedInvokeCode.value = buildDefaultInvokeCode(
    model.modelName || model.llmModel,
  );
  selectedModelId.value = String(model.id);
  draft.id = String(model.id);
  draft.invokeCode = model.invokeCode || autoGeneratedInvokeCode.value;
  draft.publishEnabled = Boolean(model.publishEnabled);
};

const confirmDiscardDraft = async () => {
  if (!isInvokeCodeDirty.value) {
    return true;
  }

  try {
    await ElMessageBox.confirm(
      '当前统一网关配置还没有保存，离开后未保存修改会丢失。',
      '未保存的更改',
      {
        confirmButtonText: '继续',
        cancelButtonText: '取消',
        type: 'warning',
      },
    );
    return true;
  } catch {
    return false;
  }
};

const loadModels = async (preferredId?: string) => {
  isLoading.value = true;
  lastErrorMessage.value = '';

  try {
    const res = await getInvokeModelList();

    if (res.errorCode !== 0) {
      modelRows.value = [];
      syncDraft();
      lastErrorMessage.value = res.message || '加载统一网关模型失败';
      return;
    }

    modelRows.value = res.data || [];
    let nextSelectedId = String(modelRows.value[0]?.id || '');

    if (
      preferredId &&
      modelRows.value.some((item) => String(item.id) === preferredId)
    ) {
      nextSelectedId = preferredId;
    } else if (
      selectedModelId.value &&
      modelRows.value.some((item) => String(item.id) === selectedModelId.value)
    ) {
      nextSelectedId = selectedModelId.value;
    }

    const nextModel = modelRows.value.find(
      (item) => String(item.id) === nextSelectedId,
    );
    checkedModelIds.value = checkedModelIds.value.filter((id) =>
      modelRows.value.some((item) => String(item.id) === id),
    );
    syncDraft(nextModel || null);
  } catch (error) {
    modelRows.value = [];
    syncDraft();
    lastErrorMessage.value =
      (error as Error)?.message || '加载统一网关模型失败';
  } finally {
    isLoading.value = false;
  }
};

const loadPublishBaseUrl = async () => {
  try {
    const res = await api.get('/api/v1/sysOption/list', {
      params: {
        keys: ['chat_publish_base_url'],
      },
    });
    if (res.errorCode === 0) {
      publishBaseUrl.value = String(
        res.data?.chat_publish_base_url || '',
      ).trim();
    }
  } catch {
    publishBaseUrl.value = '';
  }
};

const handleSelectModel = async (model: llmType) => {
  if (String(model.id) === selectedModelId.value) {
    return;
  }

  if (!(await confirmDiscardDraft())) {
    return;
  }

  syncDraft(model);
};

const copyText = async (value: string) => {
  if (!navigator?.clipboard?.writeText) {
    return;
  }
  await navigator.clipboard.writeText(value);
  ElMessage.success('已复制');
};

const syncCheckedModelIds = () => {
  const validIds = new Set(modelRows.value.map((item) => String(item.id)));
  checkedModelIds.value = checkedModelIds.value.filter((id) =>
    validIds.has(id),
  );
};

const applyUpdatedModel = (
  updatedModel: llmType,
  options?: {
    draftInvokeCode?: string;
    preserveInvokeCodeDraft?: boolean;
  },
) => {
  syncCheckedModelIds();
  modelRows.value = modelRows.value.map((item) =>
    String(item.id) === String(updatedModel.id) ? updatedModel : item,
  );

  if (String(updatedModel.id) !== selectedModelId.value) {
    return;
  }

  autoGeneratedInvokeCode.value = buildDefaultInvokeCode(
    updatedModel.modelName || updatedModel.llmModel,
  );
  draft.id = String(updatedModel.id);
  draft.publishEnabled = Boolean(updatedModel.publishEnabled);
  draft.invokeCode = options?.preserveInvokeCodeDraft
    ? options.draftInvokeCode || ''
    : updatedModel.invokeCode || autoGeneratedInvokeCode.value;
};

const applyUpdatedModels = (
  updatedModels: llmType[],
  options?: {
    draftInvokeCode?: string;
    preserveInvokeCodeDraft?: boolean;
  },
) => {
  if (updatedModels.length === 0) {
    return;
  }

  const updatedMap = new Map(
    updatedModels.map((item) => [String(item.id), item] as const),
  );
  modelRows.value = modelRows.value.map(
    (item) => updatedMap.get(String(item.id)) || item,
  );
  syncCheckedModelIds();

  const currentModel = updatedMap.get(selectedModelId.value);
  if (!currentModel) {
    return;
  }

  autoGeneratedInvokeCode.value = buildDefaultInvokeCode(
    currentModel.modelName || currentModel.llmModel,
  );
  draft.id = String(currentModel.id);
  draft.publishEnabled = Boolean(currentModel.publishEnabled);
  draft.invokeCode = options?.preserveInvokeCodeDraft
    ? options.draftInvokeCode || ''
    : currentModel.invokeCode || autoGeneratedInvokeCode.value;
};

const handleToggleModelChecked = (modelId: string, checked: boolean) => {
  const next = new Set(checkedModelIds.value);
  if (checked) {
    next.add(modelId);
  } else {
    next.delete(modelId);
  }
  checkedModelIds.value = [...next];
};

const handleToggleVisibleChecked = (checked: boolean | number | string) => {
  const next = new Set(checkedModelIds.value);
  for (const modelId of visibleModelIds.value) {
    if (checked) {
      next.add(modelId);
    } else {
      next.delete(modelId);
    }
  }
  checkedModelIds.value = [...next];
};

const handlePublishToggle = async (nextValue: boolean | number | string) => {
  if (!draft.id || !selectedModel.value || isPublishing.value) {
    return;
  }

  const publishEnabled = Boolean(nextValue);
  const previousValue = draft.publishEnabled;
  const draftInvokeCode = draft.invokeCode;
  const preserveInvokeCodeDraft = isInvokeCodeDirty.value;

  draft.publishEnabled = publishEnabled;
  isPublishing.value = true;

  try {
    const payload: ModelInvokeConfigPayload = {
      id: draft.id,
      invokeCode: draft.invokeCode.trim(),
      publishEnabled,
    };
    const res = await updateModelInvokeConfig(payload);

    if (res.errorCode !== 0) {
      draft.publishEnabled = previousValue;
      ElMessage.error(res.message || '更新发布状态失败');
      return;
    }

    applyUpdatedModel(res.data as llmType, {
      preserveInvokeCodeDraft,
      draftInvokeCode,
    });
    ElMessage.success(publishEnabled ? '已发布该模型' : '已关闭该模型发布');
    emit('updated');
  } finally {
    isPublishing.value = false;
  }
};

const handleBatchPublish = async () => {
  const targetIds = checkedModelIds.value;

  if (targetIds.length === 0) {
    ElMessage.warning('请先选择模型');
    return;
  }

  const publishEnabled = !shouldBatchUnpublish.value;
  const actionText = publishEnabled ? '发布' : '取消发布';
  const scopeText = `所选 ${targetIds.length} 个模型`;

  try {
    await ElMessageBox.confirm(
      `确认${actionText}${scopeText}吗？`,
      `批量${actionText}`,
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      },
    );
  } catch {
    return;
  }

  const draftInvokeCode = draft.invokeCode;
  const preserveInvokeCodeDraft = isInvokeCodeDirty.value;
  isBatchPublishing.value = true;

  try {
    const payload: BatchModelInvokePublishPayload = {
      ids: targetIds,
      publishEnabled,
    };
    const res = await batchUpdateInvokePublishStatus(payload);

    if (res.errorCode !== 0) {
      ElMessage.error(res.message || `批量${actionText}失败`);
      return;
    }

    applyUpdatedModels((res.data || []) as llmType[], {
      preserveInvokeCodeDraft,
      draftInvokeCode,
    });
    checkedModelIds.value = [];
    ElMessage.success(`已${actionText}${scopeText}`);
    emit('updated');
  } finally {
    isBatchPublishing.value = false;
  }
};

const saveInvokeConfig = async () => {
  if (!draft.id) {
    return;
  }

  isSaving.value = true;

  try {
    const payload: ModelInvokeConfigPayload = {
      id: draft.id,
      invokeCode: draft.invokeCode.trim(),
      publishEnabled: Boolean(selectedModel.value?.publishEnabled),
    };
    const res = await updateModelInvokeConfig(payload);

    if (res.errorCode !== 0) {
      ElMessage.error(res.message || '保存统一网关配置失败');
      return;
    }

    applyUpdatedModel(res.data as llmType);
    ElMessage.success(res.message || '统一网关配置已保存');
    emit('updated');
  } finally {
    isSaving.value = false;
  }
};

const resetDraft = () => {
  syncDraft(selectedModel.value || null);
};

onMounted(() => {
  loadPublishBaseUrl();
  loadModels();
});

defineExpose({
  async reloadData(preferredId?: string) {
    await loadModels(preferredId);
  },
  async confirmBeforeLeave() {
    return confirmDiscardDraft();
  },
});
</script>

<template>
  <section class="gateway-workspace">
    <aside class="gateway-workspace__sidebar">
      <div class="gateway-workspace__filters">
        <ElInput
          v-model.trim="filterState.keyword"
          clearable
          placeholder="搜索模型名、调用标识"
        />
        <div class="gateway-workspace__batch">
          <ElCheckbox
            :model-value="isAllVisibleChecked"
            :indeterminate="isPartiallyVisibleChecked"
            @change="handleToggleVisibleChecked"
          >
            全选当前筛选
          </ElCheckbox>
          <ElButton
            size="small"
            type="primary"
            plain
            :loading="isBatchPublishing"
            :disabled="checkedCount === 0"
            @click="handleBatchPublish"
          >
            {{ batchActionLabel }}
          </ElButton>
        </div>
      </div>

      <div v-if="isLoading" class="gateway-workspace__state">
        正在加载可发布模型...
      </div>
      <div
        v-else-if="lastErrorMessage"
        class="gateway-workspace__state gateway-workspace__state--error"
      >
        {{ lastErrorMessage }}
      </div>
      <div v-else-if="modelRows.length === 0" class="gateway-workspace__empty">
        <ElEmpty description="还没有聊天模型，先去已配置模型里新增一个。" />
      </div>
      <div
        v-else-if="filteredModels.length === 0"
        class="gateway-workspace__empty"
      >
        <ElEmpty description="没有符合条件的模型，试试调整搜索条件。" />
      </div>

      <div v-else class="gateway-workspace__list">
        <div
          v-for="model in filteredModels"
          :key="model.id"
          class="gateway-model-item"
          :class="{ 'is-active': String(model.id) === selectedModelId }"
          role="button"
          tabindex="0"
          @click="handleSelectModel(model)"
          @keydown.enter.prevent="handleSelectModel(model)"
          @keydown.space.prevent="handleSelectModel(model)"
        >
          <ElCheckbox
            class="gateway-model-item__checkbox"
            :model-value="checkedModelIds.includes(String(model.id))"
            @click.stop
            @change="
              (checked) =>
                handleToggleModelChecked(String(model.id), Boolean(checked))
            "
          />
          <ModelProviderBadge
            :icon="model.modelProvider?.icon || model.aiLlmProvider?.icon"
            :provider-name="
              model.modelProvider?.providerName ||
              model.aiLlmProvider?.providerName
            "
            :provider-type="
              model.modelProvider?.providerType ||
              model.aiLlmProvider?.providerType
            "
            :size="44"
          />
          <div class="gateway-model-item__content">
            <strong>{{ model.title }}</strong>
          </div>
          <ElTag
            :type="model.publishEnabled ? 'success' : 'info'"
            effect="plain"
            size="small"
          >
            {{ model.publishEnabled ? '已发布' : '未发布' }}
          </ElTag>
        </div>
      </div>
    </aside>

    <section class="gateway-workspace__detail">
      <div
        v-if="!selectedModel"
        class="gateway-workspace__empty gateway-workspace__empty--detail"
      >
        <ElEmpty description="请选择一个聊天模型开始配置统一网关。" />
      </div>

      <div v-else class="gateway-workspace__detail-body">
        <section class="gateway-card gateway-card--hero">
          <div class="gateway-hero__title">
            <ModelProviderBadge
              :icon="
                selectedModel.modelProvider?.icon ||
                selectedModel.aiLlmProvider?.icon
              "
              :provider-name="
                selectedModel.modelProvider?.providerName ||
                selectedModel.aiLlmProvider?.providerName
              "
              :provider-type="
                selectedModel.modelProvider?.providerType ||
                selectedModel.aiLlmProvider?.providerType
              "
              :size="48"
            />
            <div class="gateway-hero__title-text">
              <h3>{{ selectedModel.title }}</h3>
              <span
                v-if="!draft.publishEnabled"
                class="gateway-card__warning-text"
              >
                未发布
              </span>
            </div>
          </div>
          <div class="gateway-card__tags">
            <div class="gateway-publish-inline">
              <span class="gateway-publish-inline__label">API 发布</span>
              <ElSwitch
                :model-value="draft.publishEnabled"
                inline-prompt
                active-text="开"
                inactive-text="关"
                :loading="isPublishing"
                @change="handlePublishToggle"
              />
            </div>
          </div>
        </section>

        <ElAlert
          v-for="hint in statusHints"
          :key="hint"
          type="warning"
          :closable="false"
          :title="hint"
        />

        <section class="gateway-card">
          <div class="gateway-card__head">
            <h4>模型映射配置</h4>
          </div>

          <ElForm
            label-position="top"
            class="gateway-form gateway-form--compact"
          >
            <div class="gateway-form__grid">
              <ElFormItem label="上游模型 ID">
                <ElInput :model-value="upstreamModelName" disabled />
              </ElFormItem>
              <ElFormItem label="服务商类型">
                <ElInput :model-value="providerType" disabled />
              </ElFormItem>
            </div>
            <div class="gateway-form__invoke-row">
              <ElFormItem label="对外模型标识">
                <ElInput
                  v-model.trim="draft.invokeCode"
                  placeholder="默认按模型 ID 生成，可手动修改"
                />
              </ElFormItem>
              <div class="gateway-form__actions">
                <ElButton :disabled="!isInvokeCodeDirty" @click="resetDraft">
                  重置改动
                </ElButton>
                <ElButton
                  type="primary"
                  :loading="isSaving"
                  :disabled="!isInvokeCodeDirty"
                  @click="saveInvokeConfig"
                >
                  保存配置
                </ElButton>
              </div>
            </div>
          </ElForm>
        </section>

        <section class="gateway-card">
          <div class="gateway-card__head">
            <div>
              <h4>调用信息</h4>
              <p>统一网关固定走 OpenAI-compatible chat/completions 协议。</p>
            </div>
            <ElButton text size="small" @click="copyText(apiEndpoint)">
              复制地址
            </ElButton>
          </div>

          <div class="gateway-info-grid">
            <div class="gateway-info-item">
              <span>调用地址</span>
              <strong>{{ apiEndpoint }}</strong>
            </div>
            <div class="gateway-info-item">
              <span>鉴权 Header</span>
              <strong>{{ authExample }}</strong>
            </div>
          </div>
        </section>

        <section class="gateway-card">
          <div class="gateway-card__head">
            <div>
              <h4>能力说明</h4>
              <p>这里展示的是统一网关可透传的当前模型能力。</p>
            </div>
          </div>
          <div class="gateway-card__tags">
            <ElTag v-for="tag in capabilityTags" :key="tag" effect="plain">
              {{ tag }}
            </ElTag>
          </div>
        </section>

        <section class="gateway-card">
          <div class="gateway-card__head">
            <div>
              <h4>调用示例</h4>
              <p>示例中的 `model` 固定使用当前配置的 invokeCode。</p>
            </div>
          </div>

          <div class="gateway-example-list">
            <article class="gateway-example-item">
              <div class="gateway-example-item__head">
                <h5>curl</h5>
                <ElButton text size="small" @click="copyText(curlExample)">
                  复制
                </ElButton>
              </div>
              <pre>{{ curlExample }}</pre>
            </article>
            <article class="gateway-example-item">
              <div class="gateway-example-item__head">
                <h5>JavaScript</h5>
                <ElButton text size="small" @click="copyText(jsExample)">
                  复制
                </ElButton>
              </div>
              <pre>{{ jsExample }}</pre>
            </article>
            <article class="gateway-example-item">
              <div class="gateway-example-item__head">
                <h5>Python</h5>
                <ElButton text size="small" @click="copyText(pythonExample)">
                  复制
                </ElButton>
              </div>
              <pre>{{ pythonExample }}</pre>
            </article>
          </div>
        </section>
      </div>
    </section>
  </section>
</template>

<style scoped>
.gateway-workspace {
  display: grid;
  grid-template-columns: 320px minmax(0, 1fr);
  gap: 16px;
  height: calc(100vh - 240px);
  min-height: 0;
}

.gateway-workspace__sidebar,
.gateway-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 18px;
  background: hsl(var(--surface-panel) / 92%);
  border: 1px solid hsl(var(--divider-faint) / 54%);
  border-radius: 16px;
}

.gateway-workspace__sidebar {
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.gateway-card__head h4,
.gateway-card--hero h3,
.gateway-example-item__head h5 {
  margin: 0;
  color: hsl(var(--text-strong));
}

.gateway-card__head p,
.gateway-card--hero p,
.gateway-form__switch-row span,
.gateway-workspace__state {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.6;
  color: hsl(var(--text-muted));
}

.gateway-card__warning-text {
  display: inline-flex;
  align-items: center;
  font-size: 14px;
  font-weight: 700;
  line-height: 1;
  color: var(--el-color-warning);
}

.gateway-workspace__filters {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.gateway-workspace__batch {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  background: hsl(var(--surface-contrast-soft) / 48%);
  border: 1px solid hsl(var(--divider-faint) / 40%);
  border-radius: 14px;
}

.gateway-workspace__list {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
  overflow: auto;
}

.gateway-model-item {
  display: flex;
  gap: 12px;
  align-items: center;
  width: 100%;
  padding: 12px;
  text-align: left;
  background: hsl(var(--surface-panel));
  border: 1px solid hsl(var(--divider-faint) / 50%);
  border-radius: 14px;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease,
    transform 0.2s ease;
}

.gateway-model-item:hover {
  border-color: hsl(var(--primary) / 30%);
  box-shadow: 0 16px 30px -28px hsl(var(--foreground) / 34%);
  transform: translateY(-1px);
}

.gateway-model-item.is-active {
  background: hsl(var(--primary) / 7%);
  border-color: hsl(var(--primary) / 42%);
}

.gateway-model-item__content {
  display: flex;
  flex: 1;
  flex-direction: column;
  justify-content: center;
  min-width: 0;
}

.gateway-model-item__checkbox {
  flex-shrink: 0;
}

.gateway-model-item__content strong {
  display: flex;
  align-items: center;
  min-height: 24px;
  font-size: 14px;
  line-height: 1.2;
  color: hsl(var(--text-strong));
}

.gateway-model-item__content span {
  font-size: 12px;
  line-height: 1.5;
  color: hsl(var(--text-muted));
  word-break: break-all;
}

.gateway-workspace__detail {
  height: 100%;
  min-height: 0;
  overflow: auto;
}

.gateway-workspace__detail-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.gateway-card--hero {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

.gateway-hero__title {
  display: flex;
  gap: 14px;
  align-items: center;
}

.gateway-hero__title-text {
  display: flex;
  gap: 10px;
  align-items: center;
}

.gateway-hero__title-text h3 {
  font-weight: 700;
}

.gateway-card__head,
.gateway-example-item__head {
  display: flex;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
}

.gateway-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.gateway-publish-inline {
  display: flex;
  gap: 10px;
  align-items: center;
  justify-content: flex-end;
}

.gateway-publish-inline__label {
  font-size: 13px;
  font-weight: 600;
  color: hsl(var(--text-muted));
}

.gateway-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.gateway-form--compact {
  gap: 12px;
}

.gateway-form__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 12px;
}

.gateway-form__switch-row {
  display: flex;
  gap: 12px;
  align-items: center;
  min-height: 32px;
}

.gateway-form__invoke-row {
  display: flex;
  gap: 12px;
  align-items: flex-end;
}

.gateway-form__invoke-row :deep(.el-form-item) {
  flex: 1;
  margin-bottom: 0;
}

.gateway-form__actions {
  display: flex;
  flex-shrink: 0;
  gap: 12px;
  justify-content: flex-end;
}

.gateway-info-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 12px;
}

.gateway-info-item,
.gateway-example-item {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background: hsl(var(--surface-contrast-soft) / 56%);
  border: 1px solid hsl(var(--divider-faint) / 48%);
  border-radius: 14px;
}

.gateway-info-item span {
  font-size: 12px;
  color: hsl(var(--text-muted));
}

.gateway-info-item strong {
  font-size: 13px;
  line-height: 1.6;
  color: hsl(var(--text-strong));
  word-break: break-all;
}

.gateway-example-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.gateway-example-item pre {
  padding: 14px;
  margin: 0;
  overflow: auto;
  font-size: 12px;
  line-height: 1.7;
  color: hsl(var(--text-strong));
  background: hsl(var(--surface-panel));
  border: 1px solid hsl(var(--divider-faint) / 40%);
  border-radius: 12px;
}

.gateway-workspace__empty,
.gateway-workspace__state {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: center;
}

.gateway-workspace__empty--detail {
  min-height: 420px;
  padding: 18px;
  background: hsl(var(--surface-panel) / 92%);
  border: 1px solid hsl(var(--divider-faint) / 54%);
  border-radius: 16px;
}

.gateway-workspace__state--error {
  color: hsl(var(--danger));
}

@media (max-width: 1200px) {
  .gateway-workspace {
    grid-template-columns: minmax(0, 1fr);
  }
}

@media (max-width: 900px) {
  .gateway-card--hero,
  .gateway-card__head,
  .gateway-example-item__head {
    flex-direction: column;
    align-items: flex-start;
  }

  .gateway-form__grid,
  .gateway-info-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  .gateway-form__switch-row {
    flex-direction: column;
    align-items: flex-start;
  }

  .gateway-form__invoke-row {
    flex-direction: column;
    align-items: stretch;
  }

  .gateway-publish-inline {
    flex-direction: column;
    align-items: flex-start;
  }

  .gateway-hero__title {
    width: 100%;
  }
}
</style>
