<script setup lang="ts">
import type {
  SkillMcpToolManifest,
  SkillToolBinding,
  SkillToolOption,
  SkillToolType,
} from './types';

import { computed, onBeforeUnmount, reactive, ref, watch } from 'vue';

import { EasyFlowPanelModal } from '@easyflow/common-ui';

import {
  ArrowDown,
  ArrowRight,
  Connection,
  Link,
  Refresh,
  Search,
  Share,
} from '@element-plus/icons-vue';
import {
  ElButton,
  ElCheckbox,
  ElEmpty,
  ElInput,
  ElMessage,
  ElSwitch,
  ElTag,
} from 'element-plus';

import {
  getSkillMcpTools,
  getSkillToolOptions,
  updateSkillToolBindings,
} from './api';
import { resolveSkillApiErrorMessage } from './skill-api-error';

interface McpState {
  data?: SkillMcpToolManifest;
  error?: string;
  expanded?: boolean;
  loading?: boolean;
}

const props = withDefaults(
  defineProps<{
    bindings?: SkillToolBinding[];
    hasToolUpdate?: boolean;
    modelValue: boolean;
    skillId: number | string;
  }>(),
  { bindings: () => [], hasToolUpdate: false },
);

const emit = defineEmits<{
  saved: [bindings: SkillToolBinding[]];
  'update:modelValue': [value: boolean];
}>();

const typeOptions: Array<{
  icon: typeof Share;
  label: string;
  value: SkillToolType;
}> = [
  { icon: Share, label: '工作流', value: 'WORKFLOW' },
  { icon: Connection, label: '插件工具', value: 'PLUGIN' },
  { icon: Link, label: 'MCP', value: 'MCP' },
];
const activeType = ref<SkillToolType>('WORKFLOW');
const keyword = ref('');
const loading = ref(false);
const saving = ref(false);
const loadError = ref('');
const actionError = ref('');
const options = ref<SkillToolOption[]>([]);
const selected = ref<SkillToolBinding[]>([]);
const mcpStates = reactive<Record<string, McpState>>({});
const confirmedMcpManifests = reactive<Record<string, string>>({});
let optionRequest = 0;
let searchTimer: ReturnType<typeof setTimeout> | undefined;

const dialogVisible = computed({
  get: () => props.modelValue,
  set: (value) => {
    if (!value && saving.value) return;
    emit('update:modelValue', value);
  },
});
const selectedCount = computed(() => selected.value.length);
const totalToolCount = computed(() =>
  selected.value.reduce(
    (total, binding) => total + bindingToolCount(binding),
    0,
  ),
);
const hitlCount = computed(
  () => selected.value.filter((binding) => effectiveHitl(binding)).length,
);
const invalidBindings = computed(() =>
  selected.value.filter(
    (binding) => binding.resourceSummary?.available === false,
  ),
);
const unresolvedMcpBindings = computed(() =>
  selected.value.filter((binding) => {
    if (binding.toolType !== 'MCP') return false;
    const state = mcpStates[String(binding.targetId)];
    if (state?.loading || state?.error || !state?.data) return true;
    return !(
      state.data.manifestHash === binding.mcpToolManifestHash ||
      confirmedMcpManifests[String(binding.targetId)] ===
        state.data.manifestHash
    );
  }),
);
const saveDisabled = computed(
  () =>
    saving.value ||
    totalToolCount.value > 20 ||
    invalidBindings.value.length > 0 ||
    unresolvedMcpBindings.value.length > 0,
);
const currentRows = computed(() => {
  const rows = [...options.value];
  for (const binding of selected.value) {
    if (binding.toolType !== activeType.value) continue;
    if (rows.some((item) => sameTarget(item, binding))) continue;
    rows.push(optionFromBinding(binding));
  }
  return rows;
});

function bindingKey(binding: Pick<SkillToolBinding, 'targetId' | 'toolType'>) {
  return `${binding.toolType}:${binding.targetId}`;
}

function sameTarget(
  left: Pick<SkillToolBinding, 'targetId' | 'toolType'>,
  right: Pick<SkillToolBinding, 'targetId' | 'toolType'>,
) {
  return bindingKey(left) === bindingKey(right);
}

function cloneBinding(binding: SkillToolBinding): SkillToolBinding {
  return {
    hitlEnabled: Boolean(binding.hitlEnabled),
    id: binding.id,
    mcpToolCount: binding.mcpToolCount,
    mcpToolManifestHash: binding.mcpToolManifestHash,
    resourceSummary: binding.resourceSummary
      ? { ...binding.resourceSummary }
      : undefined,
    sortNo: binding.sortNo,
    targetId: binding.targetId,
    toolType: binding.toolType,
  };
}

function optionFromBinding(binding: SkillToolBinding): SkillToolOption {
  return {
    approvalRequired: binding.resourceSummary?.approvalRequired,
    available: binding.resourceSummary?.available !== false,
    description: binding.resourceSummary?.description,
    knownToolCount:
      binding.mcpToolCount ?? binding.resourceSummary?.toolCount ?? 1,
    targetId: binding.targetId,
    title: binding.resourceSummary?.title || '当前资源不可用',
    toolType: binding.toolType,
  };
}

function selectedBinding(item: SkillToolOption) {
  return selected.value.find((binding) => sameTarget(binding, item));
}

function bindingTitle(binding: SkillToolBinding) {
  const option = options.value.find((item) => sameTarget(item, binding));
  return (
    option?.title ||
    binding.resourceSummary?.title ||
    typeLabel(binding.toolType)
  );
}

function typeLabel(type: SkillToolType) {
  return typeOptions.find((item) => item.value === type)?.label || type;
}

function effectiveHitl(binding: SkillToolBinding) {
  return Boolean(
    binding.hitlEnabled || binding.resourceSummary?.approvalRequired,
  );
}

function bindingToolCount(binding: SkillToolBinding) {
  if (binding.toolType !== 'MCP') return 1;
  return Math.max(
    0,
    mcpStates[String(binding.targetId)]?.data?.toolCount ??
      binding.mcpToolCount ??
      binding.resourceSummary?.toolCount ??
      0,
  );
}

function mcpState(targetId: number | string) {
  const key = String(targetId);
  return (mcpStates[key] ||= {});
}

function mcpManifestChanged(binding: SkillToolBinding) {
  const data = mcpState(binding.targetId).data;
  return Boolean(
    data?.manifestHash && data.manifestHash !== binding.mcpToolManifestHash,
  );
}

function resetDialog() {
  activeType.value = 'WORKFLOW';
  keyword.value = '';
  actionError.value = '';
  selected.value = props.bindings.map((binding) => cloneBinding(binding));
  for (const key of Object.keys(mcpStates)) delete mcpStates[key];
  for (const key of Object.keys(confirmedMcpManifests))
    delete confirmedMcpManifests[key];
  void loadOptions();
  for (const binding of selected.value) {
    if (binding.toolType === 'MCP') void loadMcp(binding.targetId);
  }
}

async function loadOptions() {
  const request = ++optionRequest;
  loading.value = true;
  loadError.value = '';
  try {
    const response = await getSkillToolOptions({
      keyword: keyword.value.trim() || undefined,
      pageNum: 1,
      pageSize: 100,
      toolType: activeType.value,
    });
    if (request !== optionRequest) return;
    if (response.errorCode !== 0) throw response;
    options.value = response.data?.records || [];
  } catch (error) {
    if (request !== optionRequest) return;
    options.value = [];
    loadError.value = resolveSkillApiErrorMessage(
      error,
      '候选加载失败，请重试',
    );
  } finally {
    if (request === optionRequest) loading.value = false;
  }
}

async function loadMcp(targetId: number | string, force = false) {
  const state = mcpState(targetId);
  if (state.loading || (!force && state.data)) return state.data;
  state.loading = true;
  state.error = '';
  try {
    const response = await getSkillMcpTools(targetId);
    if (response.errorCode !== 0) throw response;
    state.data = response.data;
    return state.data;
  } catch (error) {
    state.error = resolveSkillApiErrorMessage(
      error,
      '工具清单加载失败，请重试',
    );
  } finally {
    state.loading = false;
  }
}

async function toggleCandidate(item: SkillToolOption) {
  if (!item.available || saving.value) return;
  const existing = selectedBinding(item);
  if (existing) {
    selected.value = selected.value.filter(
      (binding) => !sameTarget(binding, item),
    );
    return;
  }
  let manifest: SkillMcpToolManifest | undefined;
  if (item.toolType === 'MCP') {
    manifest = await loadMcp(item.targetId);
    if (!manifest) return;
    confirmedMcpManifests[String(item.targetId)] = manifest.manifestHash;
  }
  selected.value.push({
    hitlEnabled: Boolean(item.approvalRequired),
    mcpToolCount: manifest?.toolCount,
    mcpToolManifestHash: manifest?.manifestHash,
    resourceSummary: {
      approvalRequired: item.approvalRequired,
      available: item.available,
      description: item.description,
      title: item.title,
      toolCount: manifest?.toolCount ?? item.knownToolCount ?? 1,
    },
    sortNo: selected.value.length,
    targetId: item.targetId,
    toolType: item.toolType,
  });
}

function removeBinding(binding: SkillToolBinding) {
  selected.value = selected.value.filter((item) => !sameTarget(item, binding));
}

async function toggleMcpExpanded(targetId: number | string) {
  const state = mcpState(targetId);
  state.expanded = !state.expanded;
  if (state.expanded) await loadMcp(targetId);
}

function acceptLatestManifest(binding: SkillToolBinding) {
  const data = mcpState(binding.targetId).data;
  if (!data) return;
  confirmedMcpManifests[String(binding.targetId)] = data.manifestHash;
  selected.value = selected.value.map((item) =>
    sameTarget(item, binding)
      ? {
          ...item,
          mcpToolCount: data.toolCount,
          mcpToolManifestHash: data.manifestHash,
          resourceSummary: item.resourceSummary
            ? { ...item.resourceSummary, toolCount: data.toolCount }
            : item.resourceSummary,
        }
      : item,
  );
}

async function save() {
  if (saveDisabled.value) return;
  saving.value = true;
  actionError.value = '';
  try {
    const response = await updateSkillToolBindings(
      props.skillId,
      selected.value,
    );
    if (response.errorCode !== 0) throw response;
    ElMessage.success('工具绑定已保存');
    emit('saved', response.data || []);
    emit('update:modelValue', false);
  } catch (error) {
    actionError.value = resolveSkillApiErrorMessage(error, '保存失败，请重试');
  } finally {
    saving.value = false;
  }
}

watch(
  () => props.modelValue,
  (visible) => {
    if (visible) resetDialog();
  },
  { immediate: true },
);
watch(activeType, () => {
  if (props.modelValue) void loadOptions();
});
watch(keyword, () => {
  if (searchTimer) clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    if (props.modelValue) void loadOptions();
  }, 250);
});
onBeforeUnmount(() => {
  if (searchTimer) clearTimeout(searchTimer);
});
</script>

<template>
  <EasyFlowPanelModal
    v-model:open="dialogVisible"
    centered
    :closable="!saving"
    :close-on-click-modal="false"
    :show-footer="false"
    title="工具绑定"
    width="xl"
  >
    <div class="skill-tools" :aria-busy="loading || saving">
      <div class="skill-tools__summary">
        <div class="skill-tools__metrics" aria-live="polite">
          <strong>{{ selectedCount }}</strong> 项资源
          <span aria-hidden="true">·</span>
          <strong :class="{ 'is-overflow': totalToolCount > 20 }">
            {{ totalToolCount }}
          </strong>
          / 20 个工具
          <span aria-hidden="true">·</span>
          {{ hitlCount }} 项调用前确认
        </div>
        <ElTag v-if="hasToolUpdate" type="warning" effect="plain" size="small">
          有未发布变更
        </ElTag>
      </div>

      <div v-if="actionError" class="skill-tools__error" role="alert">
        <span>{{ actionError }}</span>
        <ElButton link type="primary" @click="actionError = ''">关闭</ElButton>
      </div>

      <div class="skill-tools__body">
        <section class="skill-tools__catalog" aria-label="可绑定工具">
          <div class="skill-tools__toolbar">
            <div
              class="skill-tools__types"
              role="tablist"
              aria-label="工具类型"
            >
              <button
                v-for="type in typeOptions"
                :key="type.value"
                class="skill-tools__type"
                :class="{ 'is-active': activeType === type.value }"
                role="tab"
                type="button"
                :aria-selected="activeType === type.value"
                @click="activeType = type.value"
              >
                <component :is="type.icon" />
                {{ type.label }}
              </button>
            </div>
            <ElInput
              v-model="keyword"
              clearable
              :prefix-icon="Search"
              placeholder="搜索名称或描述"
              aria-label="搜索工具"
            />
          </div>

          <div v-loading="loading" class="skill-tools__list">
            <div v-if="loadError" class="skill-tools__state" role="alert">
              <span>{{ loadError }}</span>
              <ElButton :icon="Refresh" @click="loadOptions">重新加载</ElButton>
            </div>
            <ElEmpty
              v-else-if="!loading && currentRows.length === 0"
              :description="keyword ? '没有匹配的工具' : '暂无可绑定工具'"
              :image-size="72"
            />
            <article
              v-for="item in currentRows"
              v-else
              :key="bindingKey(item)"
              class="skill-tools__candidate"
              :class="{
                'is-disabled': !item.available,
                'is-selected': Boolean(selectedBinding(item)),
              }"
            >
              <div class="skill-tools__candidate-main">
                <ElCheckbox
                  :aria-label="`${selectedBinding(item) ? '取消绑定' : '绑定'}${item.title}`"
                  :disabled="!item.available || mcpState(item.targetId).loading"
                  :model-value="Boolean(selectedBinding(item))"
                  @change="toggleCandidate(item)"
                />
                <button
                  class="skill-tools__candidate-content"
                  type="button"
                  :disabled="!item.available"
                  @click="toggleCandidate(item)"
                >
                  <span class="skill-tools__candidate-title">{{
                    item.title
                  }}</span>
                  <span class="skill-tools__candidate-description">
                    {{ item.description || '暂无描述' }}
                  </span>
                </button>
                <div class="skill-tools__candidate-meta">
                  <ElTag
                    v-if="!item.available"
                    type="danger"
                    size="small"
                    effect="plain"
                  >
                    已失效
                  </ElTag>
                  <ElTag
                    v-if="item.approvalRequired"
                    type="warning"
                    size="small"
                    effect="plain"
                  >
                    需确认
                  </ElTag>
                  <span v-if="item.toolType !== 'MCP'">1 个工具</span>
                  <span v-else-if="mcpState(item.targetId).data">
                    {{ mcpState(item.targetId).data?.toolCount }} 个工具
                  </span>
                  <ElButton
                    v-if="item.toolType === 'MCP'"
                    link
                    type="primary"
                    :loading="mcpState(item.targetId).loading"
                    :aria-expanded="Boolean(mcpState(item.targetId).expanded)"
                    @click="toggleMcpExpanded(item.targetId)"
                  >
                    <component
                      :is="
                        mcpState(item.targetId).expanded
                          ? ArrowDown
                          : ArrowRight
                      "
                    />
                    查看工具
                  </ElButton>
                </div>
              </div>
              <div
                v-if="
                  item.toolType === 'MCP' && mcpState(item.targetId).expanded
                "
                class="skill-tools__mcp-tools"
              >
                <div
                  v-if="mcpState(item.targetId).error"
                  class="skill-tools__mcp-error"
                >
                  <span>{{ mcpState(item.targetId).error }}</span>
                  <ElButton
                    link
                    type="primary"
                    @click="loadMcp(item.targetId, true)"
                  >
                    重试
                  </ElButton>
                </div>
                <div
                  v-for="tool in mcpState(item.targetId).data?.tools || []"
                  :key="tool.name"
                  class="skill-tools__mcp-tool"
                >
                  <span>{{ tool.name }}</span>
                  <small>{{ tool.description || '暂无描述' }}</small>
                </div>
              </div>
            </article>
          </div>
        </section>

        <aside class="skill-tools__selected" aria-label="已绑定工具">
          <div class="skill-tools__selected-heading">
            <div>
              <strong>已绑定</strong>
              <span>{{ selectedCount }} 项</span>
            </div>
            <span>调用前确认可逐项设置</span>
          </div>
          <ElEmpty
            v-if="selected.length === 0"
            description="从左侧选择工具"
            :image-size="64"
          />
          <div v-else class="skill-tools__selected-list">
            <article
              v-for="binding in selected"
              :key="bindingKey(binding)"
              class="skill-tools__selected-item"
            >
              <div class="skill-tools__selected-title">
                <div>
                  <strong>{{ bindingTitle(binding) }}</strong>
                  <span>
                    {{ typeLabel(binding.toolType) }} ·
                    {{ bindingToolCount(binding) }} 个工具
                  </span>
                </div>
                <ElButton link type="danger" @click="removeBinding(binding)">
                  移除
                </ElButton>
              </div>
              <div class="skill-tools__selected-state">
                <ElTag
                  v-if="binding.resourceSummary?.available === false"
                  type="danger"
                  size="small"
                  effect="plain"
                >
                  已失效
                </ElTag>
                <ElTag
                  v-if="
                    binding.toolType === 'MCP' && mcpManifestChanged(binding)
                  "
                  type="warning"
                  size="small"
                  effect="plain"
                >
                  清单变化
                </ElTag>
                <ElButton
                  v-if="
                    binding.toolType === 'MCP' && mcpManifestChanged(binding)
                  "
                  link
                  type="primary"
                  @click="acceptLatestManifest(binding)"
                >
                  确认最新清单
                </ElButton>
                <span v-if="mcpState(binding.targetId).error" class="is-error">
                  清单加载失败
                </span>
              </div>
              <label class="skill-tools__hitl">
                <span>
                  调用前确认
                  <small v-if="binding.resourceSummary?.approvalRequired">
                    MCP 要求
                  </small>
                </span>
                <ElSwitch
                  v-model="binding.hitlEnabled"
                  :disabled="Boolean(binding.resourceSummary?.approvalRequired)"
                  :aria-label="`${bindingTitle(binding)}调用前确认`"
                />
              </label>
            </article>
          </div>
        </aside>
      </div>

      <footer class="skill-tools__footer">
        <div class="skill-tools__footer-message" aria-live="polite">
          <span v-if="totalToolCount > 20" class="is-error">
            每个 Skill 最多绑定 20 个工具
          </span>
          <span v-else-if="invalidBindings.length > 0" class="is-error">
            请移除已失效的绑定
          </span>
          <span v-else-if="unresolvedMcpBindings.length > 0">
            请等待 MCP 清单加载，并确认发生变化的清单
          </span>
          <span v-else>保存后随下一次发布生效</span>
        </div>
        <div class="skill-tools__footer-actions">
          <ElButton :disabled="saving" @click="dialogVisible = false">
            取消
          </ElButton>
          <ElButton
            type="primary"
            :disabled="saveDisabled"
            :loading="saving"
            @click="save"
          >
            保存绑定
          </ElButton>
        </div>
      </footer>
    </div>
  </EasyFlowPanelModal>
</template>

<style scoped>
.skill-tools {
  display: flex;
  flex-direction: column;
  max-height: min(720px, calc(100vh - 96px));
  min-height: 560px;
  color: hsl(var(--text-strong));
}

.skill-tools__summary,
.skill-tools__toolbar,
.skill-tools__selected-heading,
.skill-tools__footer,
.skill-tools__candidate-main,
.skill-tools__selected-title,
.skill-tools__selected-state,
.skill-tools__hitl {
  display: flex;
  align-items: center;
}

.skill-tools__summary {
  justify-content: space-between;
  padding: var(--space-3) var(--space-5);
  color: hsl(var(--text-muted));
  background: hsl(var(--surface-subtle));
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.skill-tools__metrics {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  font-size: 13px;
}

.skill-tools__metrics strong {
  color: hsl(var(--text-strong));
}

.skill-tools__body {
  display: grid;
  flex: 1;
  min-height: 0;
  grid-template-columns: minmax(0, 1fr) 320px;
}

.skill-tools__catalog,
.skill-tools__selected {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.skill-tools__selected {
  background: hsl(var(--surface-subtle));
  border-left: 1px solid hsl(var(--line-subtle));
}

.skill-tools__toolbar {
  gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.skill-tools__toolbar :deep(.el-input) {
  width: min(260px, 42%);
  margin-left: auto;
}

.skill-tools__types {
  display: inline-flex;
  gap: var(--space-1);
  padding: var(--space-1);
  background: hsl(var(--surface-contrast-soft));
  border-radius: var(--radius-toolbar);
}

.skill-tools__type {
  display: inline-flex;
  gap: var(--space-2);
  align-items: center;
  height: 32px;
  padding: 0 var(--space-3);
  font-weight: 600;
  color: hsl(var(--text-muted));
  cursor: pointer;
  background: transparent;
  border: 0;
  border-radius: var(--radius-control);
  transition:
    color var(--motion-duration-base) var(--motion-ease-standard),
    background var(--motion-duration-base) var(--motion-ease-standard);
}

.skill-tools__type svg {
  width: 15px;
  height: 15px;
}

.skill-tools__type:hover {
  color: hsl(var(--text-strong));
  background: hsl(var(--table-row-hover));
}

.skill-tools__type.is-active {
  color: hsl(var(--nav-item-active-foreground));
  background: hsl(var(--nav-item-active));
}

.skill-tools__type:focus-visible,
.skill-tools__candidate-content:focus-visible {
  outline: 2px solid hsl(var(--primary));
  outline-offset: 2px;
}

.skill-tools__list,
.skill-tools__selected-list {
  flex: 1;
  min-height: 0;
  overflow: auto;
  overscroll-behavior: contain;
}

.skill-tools__list {
  padding: var(--space-2) var(--space-4) var(--space-4);
}

.skill-tools__candidate {
  border-bottom: 1px solid hsl(var(--line-subtle));
  transition: background var(--motion-duration-fast) var(--motion-ease-standard);
}

.skill-tools__candidate:not(.is-disabled):hover {
  background: hsl(var(--table-row-hover));
}

.skill-tools__candidate.is-selected {
  background: hsl(var(--nav-item-active) / 0.55);
}

.skill-tools__candidate.is-selected:hover {
  background: hsl(var(--nav-item-active) / 0.78);
}

.skill-tools__candidate.is-disabled {
  background: hsl(var(--surface-subtle));
}

.skill-tools__candidate-main {
  gap: var(--space-3);
  min-height: 72px;
  padding: var(--space-3);
}

.skill-tools__candidate-content {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
  background: transparent;
  border: 0;
}

.skill-tools__candidate-content:disabled {
  cursor: not-allowed;
}

.skill-tools__candidate-title {
  overflow: hidden;
  font-weight: 650;
  color: hsl(var(--text-strong));
  text-overflow: ellipsis;
  white-space: nowrap;
}

.skill-tools__candidate-description {
  overflow: hidden;
  font-size: 13px;
  color: hsl(var(--text-muted));
  text-overflow: ellipsis;
  white-space: nowrap;
}

.skill-tools__candidate-meta {
  display: flex;
  flex-shrink: 0;
  gap: var(--space-2);
  align-items: center;
  font-size: 12px;
  color: hsl(var(--text-muted));
}

.skill-tools__candidate-meta :deep(.el-button) svg {
  width: 14px;
  height: 14px;
}

.skill-tools__mcp-tools {
  display: grid;
  gap: var(--space-2);
  padding: 0 var(--space-3) var(--space-3) 48px;
}

.skill-tools__mcp-tool {
  display: grid;
  grid-template-columns: minmax(128px, 0.45fr) minmax(0, 1fr);
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  background: hsl(var(--surface-subtle));
  border-radius: var(--radius-control);
}

.skill-tools__mcp-tool span {
  overflow: hidden;
  font-family: ui-monospace, sfmono-regular, menlo, monospace;
  font-size: 12px;
  font-weight: 600;
  color: hsl(var(--text-strong));
  text-overflow: ellipsis;
  white-space: nowrap;
}

.skill-tools__mcp-tool small {
  color: hsl(var(--text-muted));
}

.skill-tools__mcp-error,
.skill-tools__error,
.skill-tools__state {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  justify-content: space-between;
}

.skill-tools__error {
  padding: var(--space-2) var(--space-5);
  font-size: 13px;
  color: hsl(var(--destructive));
  background: hsl(var(--destructive) / 0.08);
  border-bottom: 1px solid hsl(var(--destructive) / 0.16);
}

.skill-tools__state {
  flex-direction: column;
  justify-content: center;
  min-height: 240px;
  color: hsl(var(--text-muted));
}

.skill-tools__selected-heading {
  justify-content: space-between;
  min-height: 65px;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.skill-tools__selected-heading > div {
  display: flex;
  gap: var(--space-2);
  align-items: baseline;
}

.skill-tools__selected-heading > span,
.skill-tools__selected-heading div span {
  font-size: 12px;
  color: hsl(var(--text-muted));
}

.skill-tools__selected-list {
  padding: var(--space-2);
}

.skill-tools__selected-item {
  padding: var(--space-3);
  background: hsl(var(--surface-panel));
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.skill-tools__selected-item:first-child {
  border-radius: var(--radius-control) var(--radius-control) 0 0;
}

.skill-tools__selected-item:last-child {
  border-bottom: 0;
  border-radius: 0 0 var(--radius-control) var(--radius-control);
}

.skill-tools__selected-title {
  gap: var(--space-2);
  justify-content: space-between;
}

.skill-tools__selected-title > div {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.skill-tools__selected-title strong {
  overflow: hidden;
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.skill-tools__selected-title span,
.skill-tools__hitl small {
  font-size: 11px;
  color: hsl(var(--text-muted));
}

.skill-tools__selected-state {
  flex-wrap: wrap;
  gap: var(--space-2);
  min-height: var(--space-3);
  margin-top: var(--space-2);
  font-size: 12px;
}

.skill-tools__hitl {
  justify-content: space-between;
  margin-top: var(--space-3);
  font-size: 12px;
}

.skill-tools__hitl > span {
  display: flex;
  flex-direction: column;
}

.skill-tools__footer {
  justify-content: space-between;
  min-height: 64px;
  padding: var(--space-3) var(--space-5);
  background: hsl(var(--modal-footer-surface));
  border-top: 1px solid hsl(var(--modal-divider));
}

.skill-tools__footer-message {
  font-size: 12px;
  color: hsl(var(--text-muted));
}

.skill-tools__footer-actions {
  display: flex;
  gap: var(--space-3);
}

.is-error,
.is-overflow {
  color: hsl(var(--destructive)) !important;
}

@media (max-width: 820px) {
  .skill-tools {
    min-height: min(680px, calc(100vh - 48px));
  }

  .skill-tools__body {
    grid-template-columns: minmax(0, 1fr);
    overflow: auto;
  }

  .skill-tools__catalog,
  .skill-tools__selected {
    min-height: 360px;
  }

  .skill-tools__selected {
    border-top: 1px solid hsl(var(--line-subtle));
    border-left: 0;
  }

  .skill-tools__toolbar {
    align-items: stretch;
    flex-direction: column;
  }

  .skill-tools__toolbar :deep(.el-input) {
    width: 100%;
  }

  .skill-tools__types {
    justify-content: space-between;
  }

  .skill-tools__type {
    flex: 1;
    justify-content: center;
  }
}
</style>
