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

import { computed, nextTick, onMounted, ref, watch } from 'vue';

import { EasyFlowPanelModal } from '@easyflow/common-ui';
import { MagicStick } from '@element-plus/icons-vue';
import hljs from 'highlight.js/lib/core';
import jsonLanguage from 'highlight.js/lib/languages/json';

import {
  ElAlert,
  ElButton,
  ElForm,
  ElFormItem,
  ElInput,
  ElMessage,
  ElSwitch,
  ElTable,
  ElTableColumn,
  ElTabPane,
  ElTabs,
  ElTag,
} from 'element-plus';

import { api } from '#/api/request';
import { $t } from '#/locales';

hljs.registerLanguage('json', jsonLanguage);

interface PropValue {
  type?: string;
  description?: string;
}

interface McpTool {
  name: string;
  description: string;
  status: boolean;
  inputSchema?: {
    properties: Record<string, PropValue>;
    required?: string[];
  };
}

interface McpEntity {
  id?: string;
  title: string;
  description: string;
  configJson: string;
  deptId: string;
  status: boolean;
  approvalRequired: boolean;
  tools: McpTool[];
}

interface McpCheckItem {
  name: string;
  status: 'FAILED' | 'SUCCESS' | 'WARNING';
  message: string;
  detail?: string;
}

interface McpServerCheckResult {
  serverName: string;
  transport: string;
  status: 'FAILED' | 'SUCCESS' | 'WARNING';
  toolCount: number;
  checks: McpCheckItem[];
}

interface McpEnvironmentCheckResult {
  overallStatus: 'FAILED' | 'SUCCESS' | 'WARNING';
  servers: McpServerCheckResult[];
}

const emit = defineEmits(['reload']);

onMounted(() => {});
defineExpose({
  openDialog,
});

const saveForm = ref<FormInstance>();
const dialogVisible = ref(false);
const isAdd = ref(true);
const btnLoading = ref(false);
const checkLoading = ref(false);
const checkResult = ref<McpEnvironmentCheckResult>();
const jsonEditorTextarea = ref<HTMLTextAreaElement>();
const jsonEditorHighlight = ref<HTMLElement>();
const jsonError = ref('');

const defaultEntity: McpEntity = {
  title: '',
  description: '',
  configJson: '',
  deptId: '',
  status: false,
  approvalRequired: false,
  tools: [],
};
const entity = ref<McpEntity>({ ...defaultEntity });

const rules = ref({
  title: [
    {
      required: true,
      message: $t('message.required'),
      trigger: 'blur',
    },
  ],
  configJson: [
    {
      required: true,
      message: $t('message.required'),
      trigger: 'blur',
    },
  ],
});

const highlightedConfigJson = computed(() => {
  const value = entity.value.configJson || '';
  return hljs.highlight(value || ' ', { language: 'json' }).value;
});

watch(
  () => entity.value.configJson,
  () => {
    validateConfigJson(false);
  },
);

function openDialog(row: Partial<McpEntity> = {}) {
  isAdd.value = !row.id;
  entity.value = { ...defaultEntity, ...row };
  checkResult.value = undefined;
  if (!isAdd.value) {
    getMcpTools(row);
  }
  dialogVisible.value = true;
  nextTick(() => {
    syncJsonEditorScroll();
    validateConfigJson(false);
  });
}

function getMcpTools(row: Partial<McpEntity>) {
  api.post('api/v1/mcp/getMcpTools', { id: row.id }).then((res) => {
    if (res.errorCode === 0) {
      entity.value.tools = res.data.tools;
    }
  });
}
function save() {
  if (!validateConfigJson(true)) {
    return;
  }
  saveForm.value?.validate((valid) => {
    if (valid) {
      btnLoading.value = true;
      api
        .post(
          isAdd.value ? 'api/v1/mcp/save' : 'api/v1/mcp/update',
          entity.value,
        )
        .then((res) => {
          btnLoading.value = false;
          if (res.errorCode === 0) {
            if (isAdd.value) {
              ElMessage.success($t('message.saveOkMessage'));
            } else {
              ElMessage.success($t('message.updateOkMessage'));
            }
            emit('reload');
            closeDialog();
          }
        })
        .catch(() => {
          btnLoading.value = false;
        });
    }
  });
}

function checkMcpConfig() {
  if (!entity.value.configJson) {
    ElMessage.warning($t('mcp.check.message.configRequired'));
    return;
  }
  if (!validateConfigJson(true)) {
    return;
  }
  checkLoading.value = true;
  api
    .post('api/v1/mcp/check', { configJson: entity.value.configJson })
    .then((res) => {
      if (res.errorCode === 0) {
        checkResult.value = res.data;
      }
    })
    .finally(() => {
      checkLoading.value = false;
    });
}

function closeDialog() {
  saveForm.value?.resetFields();
  isAdd.value = true;
  entity.value = { ...defaultEntity };
  checkResult.value = undefined;
  jsonError.value = '';
  dialogVisible.value = false;
}

function formatConfigJson() {
  if (!validateConfigJson(true)) {
    return;
  }
  entity.value.configJson = JSON.stringify(
    JSON.parse(entity.value.configJson),
    null,
    2,
  );
  nextTick(() => {
    syncJsonEditorScroll();
  });
}

function validateConfigJson(showMessage: boolean) {
  if (!entity.value.configJson) {
    jsonError.value = '';
    return true;
  }
  try {
    JSON.parse(entity.value.configJson);
    jsonError.value = '';
    return true;
  } catch (error) {
    jsonError.value =
      error instanceof Error ? error.message : $t('mcp.jsonEditor.invalid');
    if (showMessage) {
      ElMessage.warning($t('mcp.jsonEditor.invalid'));
    }
    return false;
  }
}

function syncJsonEditorScroll() {
  if (!jsonEditorTextarea.value || !jsonEditorHighlight.value) {
    return;
  }
  jsonEditorHighlight.value.scrollTop = jsonEditorTextarea.value.scrollTop;
  jsonEditorHighlight.value.scrollLeft = jsonEditorTextarea.value.scrollLeft;
}

const checkTagType = (status?: string) => {
  if (status === 'SUCCESS') {
    return 'success';
  }
  if (status === 'WARNING') {
    return 'warning';
  }
  return 'danger';
};

const checkStatusLabel = (status?: string) => {
  if (status === 'SUCCESS') {
    return $t('mcp.check.status.success');
  }
  if (status === 'WARNING') {
    return $t('mcp.check.status.warning');
  }
  return $t('mcp.check.status.failed');
};
const jsonPlaceholder = ref(`{
  "mcpServers": {
    "12306-mcp": {
      "command": "npx.cmd",
      "args": [
        "-y",
        "12306-mcp"
      ]
    }
  }
}`);
const activeName = ref('config');
</script>

<template>
  <EasyFlowPanelModal
    v-model:open="dialogVisible"
    :title="isAdd ? $t('button.add') : $t('button.edit')"
    :before-close="closeDialog"
    :confirm-loading="btnLoading"
    :confirm-text="$t('button.save')"
    :submitting="btnLoading"
    width="xl"
    @confirm="save"
  >
    <ElTabs v-model="activeName" class="demo-tabs">
      <ElTabPane :label="$t('mcp.modal.config')" name="config">
        <ElForm
          ref="saveForm"
          :model="entity"
          status-icon
          :rules="rules"
          label-position="top"
          class="easyflow-modal-form easyflow-modal-form--compact"
        >
          <ElFormItem prop="title" :label="$t('mcp.title')">
            <ElInput v-model.trim="entity.title" />
          </ElFormItem>
          <ElFormItem prop="description" :label="$t('mcp.description')">
            <ElInput v-model.trim="entity.description" />
          </ElFormItem>
          <ElFormItem prop="configJson" :label="$t('mcp.configJson')">
            <div
              class="mcp-json-editor"
              :class="{ 'mcp-json-editor--error': jsonError }"
            >
              <div class="mcp-json-editor__toolbar">
                <ElButton
                  :aria-label="$t('mcp.jsonEditor.format')"
                  :icon="MagicStick"
                  circle
                  size="small"
                  text
                  :title="$t('mcp.jsonEditor.format')"
                  @click="formatConfigJson"
                />
              </div>
              <pre
                ref="jsonEditorHighlight"
                class="mcp-json-editor__highlight"
                aria-hidden="true"
              ><code v-html="highlightedConfigJson"></code></pre>
              <textarea
                ref="jsonEditorTextarea"
                v-model="entity.configJson"
                class="mcp-json-editor__textarea"
                spellcheck="false"
                :placeholder="$t('mcp.example') + jsonPlaceholder"
                :aria-invalid="Boolean(jsonError)"
                :aria-label="$t('mcp.configJson')"
                @scroll="syncJsonEditorScroll"
              ></textarea>
            </div>
            <div v-if="jsonError" class="mcp-json-editor__error">
              {{ jsonError }}
            </div>
          </ElFormItem>
          <div class="mcp-check-actions">
            <ElButton
              type="primary"
              plain
              :loading="checkLoading"
              :disabled="checkLoading"
              @click="checkMcpConfig"
            >
              {{ $t('mcp.check.action') }}
            </ElButton>
          </div>
          <ElFormItem
            prop="approvalRequired"
            :label="$t('mcp.approvalRequired')"
          >
            <ElSwitch v-model="entity.approvalRequired" />
          </ElFormItem>
          <div v-if="checkResult" class="mcp-check-result">
            <ElAlert
              :closable="false"
              :type="
                checkResult.overallStatus === 'SUCCESS'
                  ? 'success'
                  : checkResult.overallStatus === 'WARNING'
                    ? 'warning'
                    : 'error'
              "
              :title="`${$t('mcp.check.overall')}: ${checkStatusLabel(
                checkResult.overallStatus,
              )}`"
              show-icon
            />
            <div
              v-for="server in checkResult.servers"
              :key="server.serverName"
              class="mcp-check-server"
            >
              <div class="mcp-check-server__head">
                <div>
                  <div class="mcp-check-server__title">
                    {{ server.serverName }}
                  </div>
                  <div class="mcp-check-server__meta">
                    {{ server.transport || '-' }} ·
                    {{ $t('mcp.check.toolCount') }} {{ server.toolCount || 0 }}
                  </div>
                </div>
                <ElTag :type="checkTagType(server.status)" size="small">
                  {{ checkStatusLabel(server.status) }}
                </ElTag>
              </div>
              <div class="mcp-check-list">
                <div
                  v-for="(item, index) in server.checks"
                  :key="`${item.name}-${index}`"
                  class="mcp-check-item"
                >
                  <ElTag :type="checkTagType(item.status)" size="small">
                    {{ checkStatusLabel(item.status) }}
                  </ElTag>
                  <div class="mcp-check-item__content">
                    <div class="mcp-check-item__message">
                      {{ item.message }}
                    </div>
                    <div v-if="item.detail" class="mcp-check-item__detail">
                      {{ item.detail }}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <ElFormItem prop="status" :label="$t('mcp.status')">
            <ElSwitch v-model="entity.status" />
          </ElFormItem>
        </ElForm>
      </ElTabPane>
      <div v-if="!isAdd">
        <ElTabPane :label="$t('mcp.modal.tool')" name="tool">
          <ElTable
            :data="entity.tools"
            border
            :preserve-expanded-content="true"
          >
            <ElTableColumn type="expand">
              <template #default="scope">
                <!-- 解构获取properties和required，同时做空值保护 -->
                <div
                  v-if="scope.row?.inputSchema?.properties"
                  class="params-list"
                >
                  <div
                    v-for="([propKey, propValue], index) in Object.entries(
                      scope.row.inputSchema.properties,
                    )"
                    :key="index"
                    class="params-content-container"
                  >
                    <div class="params-left-title-container">
                      <div class="content-title">
                        {{ propKey }}
                        <span
                          v-if="
                            scope.row.inputSchema.required &&
                            scope.row.inputSchema.required.includes(propKey)
                          "
                          class="required-mark"
                        >
                          *
                        </span>
                      </div>
                    </div>
                    <div class="params-desc-container">
                      <div class="content-title">
                        {{ (propValue as PropValue).type || '未知类型' }}
                      </div>
                      <div class="content-desc">
                        {{ (propValue as PropValue).description || '无描述' }}
                      </div>
                    </div>
                  </div>
                </div>
                <div v-else class="params-name">暂无属性配置</div>
              </template>
            </ElTableColumn>

            <ElTableColumn :label="$t('mcp.modal.table.availableTools')">
              <template #default="{ row }">
                <div class="content-left">
                  <span class="content-title">{{ row.name }}</span>
                  <span class="content-desc">{{ row.description }}</span>
                </div>
              </template>
            </ElTableColumn>
            <!--          <ElTableColumn :label="$t('mcp.status')">
              <template #default="{ row }">
                <ElSwitch v-model="row.status" />
              </template>
            </ElTableColumn>-->
          </ElTable>
        </ElTabPane>
      </div>
    </ElTabs>
  </EasyFlowPanelModal>
</template>

<style scoped>
.content-left {
  display: flex;
  flex-direction: column;
}

.content-title {
  font-size: 12px;
  font-style: normal;
  font-weight: 500;
  line-height: 24px;
  color: rgb(0 0 0 / 85%);
  text-align: left;
  text-transform: none;
}

.content-desc {
  font-size: 12px;
  font-style: normal;
  font-weight: 400;
  line-height: 22px;
  color: rgb(0 0 0 / 45%);
  text-align: left;
  text-transform: none;
}

.params-name {
  display: flex;
  flex: 1;
  align-items: center;
  padding: 8px;
  background-color: #fafafa;
  border: 1px solid #e6e9ee;
  border-radius: 8px;
}

.params-content-container {
  display: flex;
  flex: 1;
  flex-direction: row;
  gap: 8px;
  padding: 8px;
  border-radius: 8px;
}

.params-desc-container {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  border: 1px solid #e6e9ee;
  border-radius: 8px;
}

.params-list {
  display: flex;
  flex-direction: column;
}

.params-left-title-container {
  display: flex;
  align-items: center;
  min-width: 120px;
}

.mcp-json-editor {
  position: relative;
  width: 100%;
  min-height: 320px;
  overflow: hidden;
  font-family:
    ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
    'Courier New', monospace;
  font-size: 13px;
  line-height: 20px;
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color);
  border-radius: 8px;
  transition:
    border-color var(--el-transition-duration),
    box-shadow var(--el-transition-duration);
}

.mcp-json-editor:focus-within {
  border-color: var(--el-color-primary);
  box-shadow: 0 0 0 1px var(--el-color-primary-light-7);
}

.mcp-json-editor--error {
  border-color: var(--el-color-danger);
}

.mcp-json-editor--error:focus-within {
  border-color: var(--el-color-danger);
  box-shadow: 0 0 0 1px var(--el-color-danger-light-7);
}

.mcp-json-editor__toolbar {
  position: absolute;
  top: 6px;
  right: 6px;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  background: var(--el-fill-color-blank);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 6px;
}

.mcp-json-editor__toolbar :deep(.el-button) {
  width: 24px;
  height: 24px;
  color: var(--el-text-color-secondary);
}

.mcp-json-editor__toolbar :deep(.el-button:hover),
.mcp-json-editor__toolbar :deep(.el-button:focus-visible) {
  color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
}

.mcp-json-editor__highlight,
.mcp-json-editor__textarea {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 14px 48px 12px 12px;
  margin: 0;
  overflow: auto;
  font: inherit;
  line-height: inherit;
  tab-size: 2;
  white-space: pre;
  border: 0;
}

.mcp-json-editor__highlight {
  z-index: 1;
  color: var(--el-text-color-primary);
  pointer-events: none;
  background: transparent;
}

.mcp-json-editor__textarea {
  z-index: 2;
  color: transparent;
  caret-color: var(--el-text-color-primary);
  resize: none;
  outline: none;
  background: transparent;
}

.mcp-json-editor__textarea::placeholder {
  color: var(--el-text-color-placeholder);
}

.mcp-json-editor__textarea::selection {
  color: transparent;
  background: var(--el-color-primary-light-8);
}

.mcp-json-editor__error {
  margin-top: 8px;
  font-size: 12px;
  line-height: 18px;
  color: var(--el-color-danger);
}

.mcp-json-editor :deep(.hljs-attr) {
  color: var(--el-color-primary);
}

.mcp-json-editor :deep(.hljs-string) {
  color: var(--el-color-success);
}

.mcp-json-editor :deep(.hljs-number),
.mcp-json-editor :deep(.hljs-literal) {
  color: var(--el-color-warning);
}

.mcp-json-editor :deep(.hljs-punctuation) {
  color: var(--el-text-color-secondary);
}

.mcp-check-actions {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 16px;
}

.mcp-check-result {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin-bottom: 16px;
}

.mcp-check-server {
  padding: 12px;
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
}

.mcp-check-server__head {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 12px;
}

.mcp-check-server__title {
  font-size: 13px;
  font-weight: 600;
  color: var(--el-text-color-primary);
}

.mcp-check-server__meta,
.mcp-check-item__detail {
  margin-top: 2px;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.mcp-check-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.mcp-check-item {
  display: flex;
  gap: 8px;
  align-items: flex-start;
}

.mcp-check-item__content {
  min-width: 0;
}

.mcp-check-item__message {
  font-size: 12px;
  line-height: 20px;
  color: var(--el-text-color-primary);
}

.required-mark {
  margin-left: 2px;
  font-size: 14px;
  color: #f56c6c;
}
</style>
