<template>
  <div class="custom-tools-page" :class="{ 'editor-only': editorPageMode }">
    <div class="secondary-overlay" v-if="!secondaryVerified">
      <div class="secondary-card">
        <h3>二级密码校验</h3>
        <p class="muted">输入管理员二级密码后可管理自定义工具。</p>
        <input
          class="secondary-input"
          type="password"
          v-model="secondaryPassword"
          :disabled="secondaryLoading"
          placeholder="二级密码"
          @keyup.enter="handleVerifySecondary"
        />
        <div class="secondary-actions">
          <button type="button" class="primary" :disabled="secondaryLoading" @click="handleVerifySecondary">
            {{ secondaryLoading ? '校验中...' : '确认' }}
          </button>
          <button type="button" class="ghost" :disabled="secondaryLoading" @click="checkSecondary">重新检测</button>
        </div>
        <p v-if="secondaryError" class="secondary-error">{{ secondaryError }}</p>
      </div>
    </div>
    <!-- 列表模式 -->
    <template v-if="!editorPageMode">
      <header class="page-header">
        <div>
          <h1>自定义工具管理</h1>
          <p>每个工具一个文件夹，三层文件独立存放；仅管理员可见并可调用。</p>
        </div>
        <div class="actions">
        <button type="button" class="primary" @click="openCreateModal">新建工具</button>
        <button type="button" class="ghost" @click="refresh" :disabled="loading">{{ loading ? '刷新中...' : '刷新列表' }}</button>
        <a class="ghost" href="/static/custom_tools/guide.html" target="_blank" rel="noopener">查看开发指南</a>
          <a class="ghost" href="/admin/monitor" target="_blank" rel="noopener">返回监控</a>
        <a class="ghost" href="/admin/policy" target="_blank" rel="noopener">策略配置</a>
      </div>
      </header>

      <section class="panel" v-if="error">
        <p class="error">加载失败：{{ error }}</p>
      </section>

      <section class="panel" v-else>
        <div class="tool-list-header">
          <h2>工具列表</h2>
          <span class="muted">共 {{ tools.length }} 个</span>
        </div>
        <div v-if="!tools.length" class="empty">暂无自定义工具</div>
        <div class="tool-grid">
          <div v-for="tool in tools" :key="tool.id" class="tool-card" @click="openEditorPage(tool)">
            <div class="tool-card-title">
              <strong>{{ tool.id }}</strong>
              <span class="tag">{{ tool.category || 'custom' }}</span>
            </div>
            <p class="desc">{{ tool.description || '暂无描述' }}</p>
            <div class="meta">
              <span>参数：{{ paramCount(tool) }} 个</span>
              <span>超时：{{ tool.timeout || 30 }}s</span>
            </div>
            <div class="files">
              <span>{{ tool.execution_file || 'execution.py' }}</span>
              <span>{{ tool.return_file || 'return.json' }}</span>
            </div>
          </div>
        </div>
      </section>
    </template>

    <!-- 纯编辑页面 -->
    <section v-else class="editor-page">
      <div class="editor-page-header">
        <div class="breadcrumbs">
          <a class="ghost" href="/admin/custom-tools">返回列表</a>
        </div>
        <div class="status" v-if="loading">加载中...</div>
        <div class="status error" v-else-if="error">{{ error }}</div>
      </div>

      <div v-if="activeTool" class="editor-panel">
        <header class="drawer-header">
          <div>
            <h3>{{ activeTool.id }}</h3>
            <p>{{ activeTool.description || '未填写描述' }}</p>
          </div>
          <div class="drawer-actions">
            <button type="button" class="primary" @click="saveAll" :disabled="saving">{{ saving ? '保存中...' : '保存' }}</button>
            <button type="button" class="danger" @click="openDeleteConfirm()">删除</button>
            <button type="button" class="ghost" @click="closeEditor">关闭</button>
          </div>
        </header>

        <div class="tabs">
          <button :class="{active: tab==='definition'}" @click="tab='definition'">definition.json</button>
          <button :class="{active: tab==='execution'}" @click="tab='execution'">execution.py</button>
          <button :class="{active: tab==='return'}" @click="tab='return'">return.json</button>
          <button :class="{active: tab==='meta'}" @click="tab='meta'">meta.json</button>
        </div>

        <div class="editor">
          <textarea v-if="tab==='definition'" v-model="buffers.definition" spellcheck="false"></textarea>
          <textarea v-else-if="tab==='execution'" v-model="buffers.execution" spellcheck="false"></textarea>
          <textarea v-else-if="tab==='return'" v-model="buffers.return" spellcheck="false"></textarea>
          <textarea v-else v-model="buffers.meta" spellcheck="false"></textarea>
        </div>

        <div class="hint">
          <p>提示：execution.py 中的字典/集合需要用 <code v-pre>{{ ... }}</code> 包裹，避免被模板替换。</p>
          <p>保存后无需重启，系统会自动 reload 自定义工具。</p>
        </div>
      </div>

      <div v-else-if="!error" class="empty">正在加载工具...</div>
    </section>

    <!-- 创建工具弹窗 -->
    <transition name="fade">
      <div v-if="createModal" class="modal-backdrop">
        <div class="modal">
          <h3>创建新工具</h3>
          <label>工具 ID（小写/下划线）：<input v-model="createForm.id" /></label>
          <label>描述：<input v-model="createForm.description" /></label>
          <div class="modal-actions">
            <button type="button" class="ghost" @click="createModal=false">取消</button>
            <button type="button" class="primary" @click="createTool" :disabled="creating">{{ creating ? '创建中...' : '创建' }}</button>
          </div>
        </div>
      </div>
    </transition>

    <!-- 删除确认弹窗 -->
    <transition name="fade">
      <div v-if="confirmDeleteModal" class="modal-backdrop">
        <div class="modal">
          <h3>确认删除</h3>
          <p class="delete-tip">确定删除工具 <strong>{{ deleteTargetId }}</strong> 吗？该操作不可恢复。</p>
          <div class="modal-actions">
            <button type="button" class="ghost" @click="confirmDeleteModal=false">取消</button>
            <button type="button" class="danger" @click="performDelete" :disabled="deleting">{{ deleting ? '删除中...' : '确认删除' }}</button>
          </div>
        </div>
      </div>
    </transition>
  </div>
</template>

<script setup lang="ts">
import { onMounted, reactive, ref, watch } from 'vue';
import { useSecondaryPass } from './useSecondaryPass';

interface CustomTool {
  id: string;
  description?: string;
  parameters?: any;
  category?: string;
  icon?: string | null;
  timeout?: number;
  execution_file?: string;
  return_file?: string;
}

const { verified: secondaryVerified, loading: secondaryLoading, error: secondaryError, check: checkSecondary, verify: verifySecondary } = useSecondaryPass();
const secondaryPassword = ref('');

const tools = ref<CustomTool[]>([]);
const loading = ref(false);
const error = ref('');
const activeTool = ref<CustomTool | null>(null);
const pendingOpenId = ref('');
const editorPageMode = ref(false);
const tab = ref<'definition' | 'execution' | 'return' | 'meta'>('definition');
const saving = ref(false);
const createModal = ref(false);
const creating = ref(false);
const confirmDeleteModal = ref(false);
const deleting = ref(false);
const deleteTargetId = ref('');
const createForm = reactive({ id: '', description: '' });
const buffers = reactive({ definition: '', execution: '', return: '', meta: '' });

const refresh = async () => {
  if (!secondaryVerified.value) return;
  loading.value = true;
  error.value = '';
  try {
    const res = await fetch('/api/admin/custom-tools', { credentials: 'same-origin' });
    const data = await res.json();
    if (!data.success) throw new Error(data.error || '加载失败');
    tools.value = (data.data || []).map((t: any) => ({
      ...t,
      execution_file: (t.execution && t.execution.file) || 'execution.py',
      return_file: t.return ? 'return.json' : '（无返回层）'
    }));
    if (pendingOpenId.value) {
      const target = tools.value.find((t) => t.id === pendingOpenId.value);
      if (target) {
        await openEditorInline(target);
      } else {
        error.value = `未找到工具 ${pendingOpenId.value}`;
      }
      pendingOpenId.value = '';
    }
  } catch (e: any) {
    error.value = e.message;
  } finally {
    loading.value = false;
  }
};

const openEditorInline = async (tool: CustomTool) => {
  activeTool.value = tool;
  tab.value = 'definition';
  await loadBuffers(tool.id);
};

const openEditorPage = (tool: CustomTool) => {
  const url = `/admin/custom-tools?tool=${encodeURIComponent(tool.id)}`;
  window.open(url, '_blank', 'noopener');
};

const loadBuffers = async (id: string) => {
  if (!secondaryVerified.value) return;
  try {
    buffers.definition = await fetchText(`/api/admin/custom-tools/file?id=${encodeURIComponent(id)}&name=definition.json`);
    buffers.execution = await fetchText(`/api/admin/custom-tools/file?id=${encodeURIComponent(id)}&name=execution.py`);
    buffers.return = await fetchText(`/api/admin/custom-tools/file?id=${encodeURIComponent(id)}&name=return.json`, '');
    buffers.meta = await fetchText(`/api/admin/custom-tools/file?id=${encodeURIComponent(id)}&name=meta.json`, '');
  } catch (e: any) {
    error.value = e.message;
  }
};

const fetchText = async (url: string, fallback = ''): Promise<string> => {
  const res = await fetch(url, { credentials: 'same-origin' });
  if (res.status === 404) return fallback;
  const text = await res.text();
  return text || fallback;
};

const saveAll = async () => {
  if (!secondaryVerified.value) return;
  if (!activeTool.value) return;
  saving.value = true;
  try {
    await saveFile(activeTool.value.id, 'definition.json', buffers.definition);
    await saveFile(activeTool.value.id, 'execution.py', buffers.execution);
    await saveFile(activeTool.value.id, 'return.json', buffers.return);
    await saveFile(activeTool.value.id, 'meta.json', buffers.meta);
    await reloadRegistry();
    await refresh();
  } catch (e: any) {
    error.value = e.message;
  } finally {
    saving.value = false;
  }
};

const saveFile = async (id: string, name: string, content: string) => {
  const res = await fetch('/api/admin/custom-tools/file', {
    method: 'POST',
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ id, name, content })
  });
  const data = await res.json();
  if (!data.success) throw new Error(data.error || `保存 ${name} 失败`);
};

const reloadRegistry = async () => {
  await fetch('/api/admin/custom-tools/reload', { method: 'POST', credentials: 'same-origin' });
};

const confirmDelete = async () => {
  if (!activeTool.value) return;
  openDeleteConfirm(activeTool.value);
};

const openDeleteConfirm = (tool?: CustomTool) => {
  const target = tool || activeTool.value;
  if (!target) return;
  deleteTargetId.value = target.id;
  confirmDeleteModal.value = true;
};

const performDelete = async () => {
  if (!secondaryVerified.value) return;
  if (!deleteTargetId.value) return;
  deleting.value = true;
  try {
    const res = await fetch(`/api/admin/custom-tools?id=${encodeURIComponent(deleteTargetId.value)}`, { method: 'DELETE', credentials: 'same-origin' });
    const data = await res.json();
    if (!data.success) throw new Error(data.error || '删除失败');
    activeTool.value = null;
    confirmDeleteModal.value = false;
    deleteTargetId.value = '';
    await refresh();
  } catch (e: any) {
    error.value = e.message;
  }
  deleting.value = false;
};

const openCreateModal = () => {
  createForm.id = '';
  createForm.description = '';
  createModal.value = true;
};

const createTool = async () => {
  if (!secondaryVerified.value) return;
  const id = createForm.id.trim();
  if (!id) {
    error.value = '请填写工具 ID';
    return;
  }
  const idPattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
  if (!idPattern.test(id)) {
    error.value = '工具 ID 需以字母开头，可包含字母/数字/_/-';
    return;
  }
  creating.value = true;
  try {
    const payload = {
      id,
      description: createForm.description,
      parameters: { type: 'object', properties: {}, required: [] },
      execution_code: "print('hello')\n",
      category: 'custom'
    };
    const res = await fetch('/api/admin/custom-tools', {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    const data = await res.json();
    if (!data.success) throw new Error(data.error || '创建失败');
    createModal.value = false;
    activeTool.value = null;
    await refresh();
    window.location.href = `/admin/custom-tools?tool=${encodeURIComponent(id)}`;
  } catch (e: any) {
    error.value = e.message;
  } finally {
    creating.value = false;
  }
};

const closeEditor = () => {
  window.location.href = '/admin/custom-tools';
};

const paramCount = (tool: CustomTool) => {
  const props = tool.parameters?.properties || {};
  return Object.keys(props).length;
};

const handleVerifySecondary = async () => {
  await verifySecondary(secondaryPassword.value);
  if (secondaryVerified.value) {
    secondaryPassword.value = '';
    await refresh();
  }
};

onMounted(async () => {
  const params = new URLSearchParams(window.location.search);
  const toolId = params.get('tool');
  if (toolId) {
    pendingOpenId.value = toolId;
    editorPageMode.value = true;
  }
  await checkSecondary();
  if (secondaryVerified.value) {
    await refresh();
  }
});

watch(secondaryVerified, async (val) => {
  if (val) {
    await refresh();
  }
});
</script>

<style scoped>
/* Claude 经典：米色+白色+深棕的柔和高对比配色 */
:global(html, body) {
  min-height: 100%;
  background: radial-gradient(140% 120% at 20% 20%, rgba(239, 229, 214, 0.9), transparent),
    radial-gradient(120% 120% at 80% 0%, rgba(255, 255, 255, 0.9), transparent),
    #f8f3e8;
}
:global(#custom-tools-app) {
  min-height: 100vh;
}
.custom-tools-page {
  --sand: #f8f3e8;
  --sand-strong: #efe5d6;
  --paper: #ffffff;
  --brown: #4b3626;
  --brown-strong: #2f2015;
  --ink: #1a120b;
  --muted: #6f635a;
  --border: #e3d8c8;
  --glow: 0 14px 40px rgba(47, 32, 21, 0.12);
  --radius-lg: 18px;
  --radius-md: 12px;

  width: 100%;
  max-width: 1180px;
  margin: 0 auto;
  padding: 28px;
  font-family: 'Space Grotesk', 'Noto Sans SC', 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  color: var(--ink);
  background: transparent;
}
.custom-tools-page.editor-only {
  max-width: 1080px;
  width: min(1080px, 100%);
  padding: 20px 16px;
}
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.page-header h1 {
  margin: 0;
  letter-spacing: 0.4px;
  color: var(--brown-strong);
}
.page-header p {
  margin: 4px 0 0;
  color: var(--muted);
}
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.panel {
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 18px;
  box-shadow: var(--glow);
}
.tool-list-header {
  display: flex;
  align-items: center;
  gap: 8px;
}
.tool-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 12px;
  margin-top: 12px;
}
.tool-card {
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 14px;
  cursor: pointer;
  transition: border-color 0.2s, box-shadow 0.2s, transform 0.1s, background 0.2s;
  background: linear-gradient(180deg, #fffdf8 0%, #ffffff 50%, #fbf6ed 100%);
}
.tool-card:hover {
  border-color: var(--brown);
  box-shadow: 0 12px 30px rgba(47, 32, 21, 0.12);
  transform: translateY(-1px);
}
.tool-card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}
.tool-card-title strong {
  word-break: break-all;
  color: var(--brown-strong);
}
.desc {
  margin: 6px 0;
  color: var(--muted);
  min-height: 32px;
  word-break: break-word;
}
.meta,
.files {
  display: flex;
  gap: 12px;
  font-size: 12px;
  color: var(--muted);
  flex-wrap: wrap;
}
.files span {
  max-width: 100%;
  word-break: break-all;
}
.tag {
  background: rgba(75, 54, 38, 0.08);
  color: var(--brown-strong);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 3px 10px;
  font-size: 12px;
  white-space: nowrap;
}
.drawer-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 12px;
}
.drawer-header h3 {
  margin: 0;
}
.editor-panel {
  margin-top: 16px;
  background: var(--paper);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: 18px;
  box-shadow: var(--glow);
  width: 100%;
  box-sizing: border-box;
}
.editor-page {
  margin-top: 12px;
}
.editor-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.breadcrumbs {
  display: flex;
  gap: 8px;
  align-items: center;
}
.status {
  color: var(--muted);
}
.status.error {
  color: #dc2626;
}
.editor-only {
  max-width: 1080px;
}
.tabs {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}
.tabs button {
  padding: 8px 12px;
  border: 1px solid var(--border);
  background: #fdf8ef;
  border-radius: 10px;
  cursor: pointer;
  color: var(--muted);
}
.tabs button.active {
  border-color: var(--brown);
  color: var(--brown-strong);
  background: #fff;
  box-shadow: inset 0 0 0 1px rgba(75, 54, 38, 0.04);
}
.editor {
  flex: 1;
  min-height: 320px;
  width: 100%;
  overflow: auto;
}
.editor textarea {
  width: 100%;
  height: 100%;
  min-height: 320px;
  font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 14px;
  outline: none;
  resize: vertical;
  background: #fdf9f1;
  color: var(--ink);
  line-height: 1.5;
  box-shadow: inset 0 2px 6px rgba(47, 32, 21, 0.04);
  box-sizing: border-box;
  max-width: 100%;
}
.hint {
  font-size: 12px;
  color: var(--muted);
}
.modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(26, 18, 11, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 2100;
}
.modal {
  background: var(--paper);
  padding: 18px;
  border-radius: var(--radius-lg);
  width: 360px;
  max-width: min(420px, 90vw);
  box-sizing: border-box;
  box-shadow: 0 16px 40px rgba(47, 32, 21, 0.18);
  display: flex;
  flex-direction: column;
  gap: 12px;
  border: 1px solid var(--border);
}
.modal h3 {
  margin: 0;
  color: var(--brown-strong);
}
.modal input {
  width: 100%;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: #fdf8ef;
  box-sizing: border-box;
}
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.delete-tip {
  margin: 4px 0 8px;
  color: var(--muted);
}
.error { color: #dc2626; }
.empty { color: var(--muted); padding: 12px; }

/* 按钮样式与主站一致风格 */
.primary,
.ghost,
.danger {
  font-family: inherit;
  font-weight: 600;
  font-size: 14px;
  line-height: 1.1;
  border-radius: 12px;
  padding: 10px 18px;
}
.primary {
  background: linear-gradient(135deg, var(--claude-accent, #da7756) 0%, var(--claude-accent-strong, #bd5d3a) 100%);
  color: #fff;
  border: none;
  cursor: pointer;
  box-shadow: 0 10px 22px rgba(47, 32, 21, 0.15);
}
.primary:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.ghost {
  border: 1px solid var(--border);
  text-decoration: none;
  color: var(--brown-strong);
  background: #fffdf8;
}
.ghost:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.danger {
  background: linear-gradient(135deg, #c05621, #9c4221);
  color: #fff;
  border: none;
  cursor: pointer;
  box-shadow: 0 12px 28px rgba(156, 66, 33, 0.22);
}
.actions .primary,
.actions .ghost {
  text-decoration: none;
}

@media (max-width: 900px) {
  .drawer-content {
    max-width: 100%;
  }
}
@media (max-width: 640px) {
  .custom-tools-page {
    padding: 16px;
  }
  .page-header {
    flex-direction: column;
    align-items: flex-start;
  }
  .actions {
    width: 100%;
  }
  .tool-grid {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  }
  .drawer {
    width: 100%;
  }
  .drawer-content {
    padding: 12px;
  }
  .editor textarea {
    min-height: 220px;
  }
}

.secondary-overlay {
  position: fixed;
  inset: 0;
  background: rgba(20, 12, 5, 0.35);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 999;
}

.secondary-card {
  width: 360px;
  max-width: 90vw;
  background: #f6ecda;
  border: 1px solid rgba(118, 103, 84, 0.35);
  border-radius: 18px;
  padding: 20px;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.16);
}

.secondary-card h3 {
  margin: 0 0 8px;
}

.secondary-card p {
  margin: 0 0 12px;
  color: #5b4b35;
}

.secondary-input {
  width: 100%;
  box-sizing: border-box;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid rgba(44, 32, 19, 0.2);
  margin-bottom: 12px;
  font-size: 15px;
}

.secondary-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

.secondary-actions .ghost {
  background: transparent;
  border: 1px dashed rgba(44, 32, 19, 0.35);
  padding: 10px 14px;
  border-radius: 12px;
  cursor: pointer;
  color: #2a2013;
}

.secondary-error {
  color: #b5473d;
  margin-top: 10px;
}
</style>
