<template>
  <t-card title="🔌 MCP 服务管理" :bordered="false">
    <p class="desc-text">管理 MCP (Model Context Protocol) 服务端配置，支持 SSE 和 Stdio 两种传输方式</p>
    <div class="toolbar">
      <t-button theme="primary" size="small" @click="openAddModal">新建配置</t-button>
      <t-button variant="outline" size="small" @click="refreshConnections" :disabled="refreshLoading">{{ refreshLoading?'刷新中...':'刷新连接' }}</t-button>
      <t-button variant="outline" size="small" @click="load(currentPage)">刷新列表</t-button>
      <div style="flex:1;" />
      <span class="total-hint">共 {{ total }} 条配置</span>
    </div>

    <t-table :data="servers" :columns="columns" row-key="id"
      :pagination="{ current: currentPage, total: total, pageSize: pageSize, showJumper: true }" @page-change="onPageChange">
      <template #transport_type="{ row }"><t-tag size="small" :theme="row.transport_type==='sse'?'primary':'default'" variant="light">{{ row.transport_type==='sse'?'SSE':'Stdio' }}</t-tag></template>
      <template #displayUrl="{ row }"><span class="ellipsis" :title="getDisplayUrl(row)">{{ getDisplayUrl(row)||'-' }}</span></template>
      <template #is_active="{ row }"><t-switch v-model="row._active" size="small" @change="toggleActive(row)" /></template>
      <template #op="{ row }">
        <t-space :size="4">
          <t-button size="small" variant="text" @click="testConnection(row)" :disabled="testLoading[row.id]">{{ testLoading[row.id]?'测试中...':'测试' }}</t-button>
          <t-button size="small" variant="text" @click="openEditModal(row)">编辑</t-button>
          <t-button size="small" variant="text" theme="danger" @click="remove(row.id,row.name)">删除</t-button>
        </t-space>
      </template>
    </t-table>

    <!-- 测试结果 -->
    <div v-if="testResult.visible" class="test-toast" :style="{background:testResult.success?'var(--color-success-text)':'var(--color-error-text)'}">
      <strong>{{ testResult.success?'连接成功':'连接失败' }}</strong>
      <span v-if="testResult.message" class="test-msg">{{ testResult.message }}</span>
    </div>

    <!-- 编辑弹窗 -->
    <t-dialog v-model:visible="showModal" :header="modalMode==='add'?'新建 MCP 服务配置':'编辑 MCP 服务配置'" width="600px" :footer="false">
      <t-form label-align="top">
        <t-form-item label="服务名称 *"><t-input v-model="form.name" placeholder="如：天气查询服务" /></t-form-item>
        <t-form-item label="传输类型 *">
          <t-radio-group v-model="form.transportType">
            <t-radio-button value="sse">SSE（HTTP 服务）</t-radio-button>
            <t-radio-button value="stdio">Stdio（本地进程）</t-radio-button>
          </t-radio-group>
        </t-form-item>
        <t-form-item v-if="form.transportType==='sse'" label="Server URL *"><t-input v-model="form.url" placeholder="如：http://localhost:3001/sse" /></t-form-item>
        <t-form-item v-if="form.transportType==='sse'" label="请求头（可选）">
          <t-textarea v-model="form.headersText" :autosize="{minRows:2,maxRows:4}" placeholder="Authorization: Bearer sk-xxx\nX-API-Key: abc123" />
          <div style="font-size:11px;color:var(--color-text-secondary);margin-top:4px;">💡 敏感头（Authorization 等）已脱敏，未修改则保留原值</div>
        </t-form-item>
        <template v-if="form.transportType==='stdio'">
          <t-form-item label="Command *"><t-input v-model="form.command" placeholder="如：npx, python, node" /></t-form-item>
          <t-form-item label="Args（每行一个）"><t-textarea v-model="form.argsText" :autosize="{minRows:2,maxRows:4}" placeholder="-m\nmcp-server-fetch" /></t-form-item>
        </template>
        <t-form-item label="环境变量（可选）"><t-textarea v-model="form.envText" :autosize="{minRows:2,maxRows:3}" placeholder="KEY1=value1\nKEY2=value2" /></t-form-item>
        <t-form-item label="描述说明"><t-textarea v-model="form.description" :autosize="{minRows:2,maxRows:3}" placeholder="可选" /></t-form-item>
      </t-form>
      <div class="dialog-footer">
        <t-button variant="outline" @click="showModal=false">取消</t-button>
        <t-button theme="primary" @click="save" :loading="saving">保存</t-button>
      </div>
    </t-dialog>
  </t-card>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as api from '@/api/mcp-server'
import { toast } from '@/utils/toast'
import { formatDate } from '@/utils/format'
import { useConfirm } from '@/composables/useConfirm'

const { confirm } = useConfirm()

const servers = ref<any[]>([]); const currentPage=ref(1); const pageSize=ref(10); const total=ref(0)
const showModal=ref(false); const modalMode=ref('add'); const editId=ref<any>(null); const saving=ref(false)
const form=ref({name:'',transportType:'sse',url:'',command:'',argsText:'',envText:'',headersText:'',description:''})
const testLoading=ref<Record<string,boolean>>({}); const testResult=ref({visible:false,success:false,message:'' as any}); let testTimer:any=null
const refreshLoading=ref(false)

const columns=[{colKey:'name',title:'名称',width:130},{colKey:'transport_type',title:'传输类型',width:90},{colKey:'displayUrl',title:'URL/命令',width:200,ellipsis:true},{colKey:'is_active',title:'状态',width:70},{colKey:'description',title:'描述',width:140,ellipsis:true},{colKey:'create_time',title:'创建时间',width:160,cell:(_:any,{row}:any)=>formatDate(row.create_time)},{colKey:'op',title:'操作',width:170,}]

onMounted(()=>load())
onUnmounted(()=>{if(testTimer)clearTimeout(testTimer)})

async function load(p=1){currentPage.value=p
  try{const r=await api.listMcpServers(p,pageSize.value);if(r.success){servers.value=(r.data||[]).map((s:any)=>({...s,_active:s.is_active}));total.value=r.total||0}else toast(r.message||'查询失败','error')}catch(e:any){toast('加载失败：'+e.message,'error')}}
function onPageChange(i:{current:number;pageSize:number}){pageSize.value=i.pageSize;load(i.current)}

function createEmpty(){return{name:'',transportType:'sse',url:'',command:'',argsText:'',envText:'',headersText:'',description:''}}
function openAddModal(){modalMode.value='add';editId.value=null;form.value=createEmpty();showModal.value=true}
function openEditModal(s:any){modalMode.value='edit';editId.value=s.id
  let at='',et='',ht=''
  if(s.args){if(Array.isArray(s.args))at=s.args.join('\n');else{try{const p=JSON.parse(s.args);if(Array.isArray(p))at=p.join('\n');else at=s.args}catch{at=s.args}}}
  if(s.env_vars){if(typeof s.env_vars==='object')et=Object.entries(s.env_vars).map(([k,v])=>k+'='+v).join('\n');else{try{const p=JSON.parse(s.env_vars);if(typeof p==='object')et=Object.entries(p).map(([k,v]:any)=>k+'='+v).join('\n');else et=s.env_vars}catch{et=s.env_vars}}}
  if(s.headers){if(typeof s.headers==='object')ht=Object.entries(s.headers).map(([k,v])=>k+': '+v).join('\n');else{try{const p=JSON.parse(s.headers);if(typeof p==='object')ht=Object.entries(p).map(([k,v]:any)=>k+': '+v).join('\n');else ht=s.headers}catch{ht=s.headers}}}
  form.value={name:s.name||'',transportType:s.transport_type||'sse',url:s.server_url||'',command:s.command||'',argsText:at,envText:et,headersText:ht,description:s.description||''};showModal.value=true}

async function save(){
  const f=form.value;if(!f.name.trim()){toast('请填写服务名称','error');return}
  if(f.transportType==='sse'){if(!f.url.trim()){toast('请填写 URL','error');return}}else{if(!f.command.trim()){toast('请填写 Command','error');return}}
  const d:any={name:f.name,transportType:f.transportType,description:f.description}
  if(f.transportType==='sse'){d.serverUrl=f.url;if(f.headersText.trim()){const h:any={};f.headersText.split('\n').forEach(l=>{const t=l.trim();if(!t)return;const i=t.indexOf(':');if(i>0)h[t.substring(0,i).trim()]=t.substring(i+1).trim()});d.headers=h}}
  else{d.command=f.command;d.args=f.argsText.trim()?f.argsText.split('\n').map(s=>s.trim()).filter(Boolean):[]}
  if(f.envText.trim()){const e:any={};f.envText.split('\n').forEach(l=>{const t=l.trim();if(!t)return;const i=t.indexOf('=');if(i>0)e[t.substring(0,i)]=t.substring(i+1)});d.envVars=e}
  saving.value=true
  try{let r;if(modalMode.value==='add')r=await api.createMcpServer(d);else r=await api.updateMcpServer(editId.value,d)
    if(r.success){toast(modalMode.value==='add'?'创建成功':'更新成功','success');showModal.value=false;load(currentPage.value)}else toast(r.message||'操作失败','error')}catch(e:any){toast('保存失败：'+e.message,'error')}finally{saving.value=false}}

async function remove(id:string,name:string){if(!await confirm('确定删除「'+(name||id)+'」？'))return
  try{const r=await api.deleteMcpServer(id);if(r.success){toast('删除成功','success');load(currentPage.value)}else toast(r.message||'删除失败','error')}catch(e:any){toast('删除失败：'+e.message,'error')}}

async function toggleActive(s:any){const na=!s.is_active
  try{const r=await api.toggleMcpServer(s.id,na);if(r.success){toast(na?'已启用':'已禁用','success');s.is_active=na;s._active=na}else toast(r.message||'操作失败','error')}catch(e:any){toast('操作失败：'+e.message,'error')}}

async function testConnection(s:any){testLoading.value={...testLoading.value,[s.id]:true}
  try{const r=await api.testMcpServer(s.id);showResult(r)}catch(e:any){showResult({success:false,message:e.message})}finally{testLoading.value={...testLoading.value,[s.id]:false}}}

function showResult(r:any){if(testTimer)clearTimeout(testTimer);testResult.value={visible:true,success:r.success,message:r.message||''};testTimer=setTimeout(()=>{testResult.value.visible=false},5000)}

async function refreshConnections(){refreshLoading.value=true
  try{const r=await api.refreshMcpServers();if(r.success){toast('连接已刷新','success');load(currentPage.value)}else toast(r.message||'刷新失败','error')}catch(e:any){toast('刷新失败：'+e.message,'error')}finally{refreshLoading.value=false}}

function getDisplayUrl(s:any){if(s.transport_type==='sse')return s.server_url||'-';let d=s.command||'';if(s.args)d+=' '+(Array.isArray(s.args)?s.args.join(' '):s.args);return d||'-'}
</script>
<style scoped>
.ellipsis{max-width:220px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;}.test-toast{position:fixed;bottom:20px;right:20px;padding:12px 20px;border-radius:8px;z-index:9999;color:var(--td-text-color-anti);max-width:400px;box-shadow:0 4px 12px rgba(0,0,0,.15);}.test-msg{font-size:12px;margin-top:4px;opacity:.9;display:block;}</style>
