<script setup lang="ts">
import type {
  AgentDraftState,
  AgentOption,
  AgentValidationIssue,
} from '../types';

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

import { Close } from '@element-plus/icons-vue';
import { ElButton, ElTabPane, ElTabs } from 'element-plus';

import AgentBaseForm from './AgentBaseForm.vue';

const props = defineProps<{
  canDisableShellApproval: boolean;
  categories: AgentOption[];
  issues: AgentValidationIssue[];
  knowledges: AgentOption[];
  mcps: AgentOption[];
  mcpToolsLoading: Record<string, boolean>;
  models: AgentOption[];
  pluginTools: AgentOption[];
  state: AgentDraftState;
  workflows: AgentOption[];
}>();

const emit = defineEmits<{
  addSkills: [];
  change: [];
  closeTryout: [];
  loadMcpTools: [id: string];
  moveSkill: [skillId: number | string | undefined, offset: -1 | 1];
  removeCapability: [];
  removeSkill: [skillId: number | string | undefined];
  selectIssue: [nodeId: string];
}>();

const loadAgentBuiltinToolsForm = () => import('./AgentBuiltinToolsForm.vue');
const loadAgentInteractionForm = () => import('./AgentInteractionForm.vue');
const loadAgentKnowledgeForm = () => import('./AgentKnowledgeForm.vue');
const loadAgentSkillInspector = () => import('./AgentSkillInspector.vue');
const loadAgentToolForm = () => import('./AgentToolForm.vue');
const loadAgentTryoutPanel = () => import('./AgentTryoutPanel.vue');
const AgentBuiltinToolsForm = defineAsyncComponent(loadAgentBuiltinToolsForm);
const AgentInteractionForm = defineAsyncComponent(loadAgentInteractionForm);
const AgentKnowledgeForm = defineAsyncComponent(loadAgentKnowledgeForm);
const AgentSkillInspector = defineAsyncComponent(loadAgentSkillInspector);
const AgentToolForm = defineAsyncComponent(loadAgentToolForm);
const AgentTryoutPanel = defineAsyncComponent(loadAgentTryoutPanel);

interface AgentInteractionFormExpose {
  focusField: (field: string) => Promise<void> | void;
}

const selectedKnowledge = computed(() => {
  if (!props.state.selectedNodeId.startsWith('knowledge:')) return;
  const localId = props.state.selectedNodeId.slice('knowledge:'.length);
  return props.state.knowledgeBindings.find((item) => item.localId === localId);
});

const activeBaseTab = ref<'basic' | 'builtinTools' | 'interaction'>('basic');
const interactionForm = ref<AgentInteractionFormExpose>();
const tryoutExpanded = ref(false);
const selectedModel = computed(() =>
  props.models.find((item) => item.value === String(props.state.agent.modelId)),
);
const tryoutImageEnabled = computed(() =>
  Boolean(selectedModel.value?.raw?.supportImage),
);

function isInteractionIssue(issue?: AgentValidationIssue) {
  return issue?.field?.startsWith('interaction.');
}

async function focusIssue(issue: AgentValidationIssue) {
  if (issue.nodeId !== 'agent-base') return;
  activeBaseTab.value = isInteractionIssue(issue) ? 'interaction' : 'basic';
  if (isInteractionIssue(issue) && issue.field) {
    await nextTick();
    await interactionForm.value?.focusField(issue.field);
  }
}

function handleIssueClick(issue: AgentValidationIssue) {
  void focusIssue(issue);
  emit('selectIssue', issue.nodeId);
}

function handleMoveSkill(skillId: number | string | undefined, offset: -1 | 1) {
  emit('moveSkill', skillId, offset);
}

function handleCloseTryout() {
  tryoutExpanded.value = false;
  emit('closeTryout');
}

function handleWindowKeydown(event: KeyboardEvent) {
  if (event.key !== 'Escape' || !tryoutExpanded.value) {
    return;
  }
  event.preventDefault();
  tryoutExpanded.value = false;
}

watch(
  () => props.issues,
  (issues) => {
    const firstIssue = issues[0];
    if (firstIssue?.nodeId === 'agent-base') void focusIssue(firstIssue);
  },
  { deep: true },
);

watch(
  () => props.state.panelMode,
  (panelMode) => {
    if (panelMode !== 'tryout') {
      tryoutExpanded.value = false;
    }
  },
  { flush: 'sync' },
);

const selectedTool = computed(() => {
  if (!props.state.selectedNodeId.startsWith('tool:')) return;
  const localId = props.state.selectedNodeId.slice('tool:'.length);
  return props.state.toolBindings.find((item) => item.localId === localId);
});

const selectedToolKind = computed(() => {
  const toolType = String(selectedTool.value?.toolType || '').toUpperCase();
  if (toolType === 'WORKFLOW') return 'workflow';
  if (toolType === 'MCP') return 'mcp';
  return 'plugin';
});

const selectedToolOptions = computed(() => {
  if (selectedToolKind.value === 'workflow') return props.workflows;
  if (selectedToolKind.value === 'mcp') return props.mcps;
  return props.pluginTools;
});

watch(
  () =>
    selectedToolKind.value === 'mcp'
      ? String(selectedTool.value?.targetId || '')
      : '',
  (mcpId) => {
    if (mcpId) {
      emit('loadMcpTools', mcpId);
    }
  },
  { immediate: true },
);

let prefetchHandle: number | undefined;
let prefetchTimer: number | undefined;

onMounted(() => {
  window.addEventListener('keydown', handleWindowKeydown);
  const prefetchHiddenPanels = () => {
    void Promise.allSettled([
      loadAgentBuiltinToolsForm(),
      loadAgentInteractionForm(),
      loadAgentKnowledgeForm(),
      loadAgentSkillInspector(),
      loadAgentToolForm(),
      loadAgentTryoutPanel(),
    ]);
  };
  if (typeof window.requestIdleCallback === 'function') {
    prefetchHandle = window.requestIdleCallback(prefetchHiddenPanels, {
      timeout: 3000,
    });
    return;
  }
  prefetchTimer = window.setTimeout(prefetchHiddenPanels, 1000);
});

onBeforeUnmount(() => {
  window.removeEventListener('keydown', handleWindowKeydown);
  if (prefetchHandle !== undefined) {
    window.cancelIdleCallback(prefetchHandle);
  }
  if (prefetchTimer !== undefined) {
    window.clearTimeout(prefetchTimer);
  }
});
</script>

<template>
  <aside
    class="agent-inspector"
    :class="{
      'is-tryout': state.panelMode === 'tryout',
      'is-tryout-expanded': state.panelMode === 'tryout' && tryoutExpanded,
    }"
  >
    <template v-if="state.panelMode === 'tryout'">
      <AgentTryoutPanel
        :agent="state.agent"
        :image-enabled="tryoutImageEnabled"
        :skill-bindings="state.skillBindings"
        :tool-bindings="state.toolBindings"
        :knowledge-bindings="state.knowledgeBindings"
        :expanded="tryoutExpanded"
        @close="handleCloseTryout"
        @update:expanded="tryoutExpanded = $event"
      />
    </template>
    <template v-else>
      <header
        v-if="state.selectedNodeId !== 'skills'"
        class="agent-inspector__header"
      >
        <div>
          <div class="agent-inspector__title">
            {{ state.panelMode === 'base' ? '基座智能体' : '能力配置' }}
          </div>
          <div class="agent-inspector__subtitle">
            {{ state.panelMode === 'base' ? '核心设定' : '绑定关系' }}
          </div>
        </div>
      </header>

      <div class="agent-inspector__body">
        <div v-if="issues.length > 0" class="agent-inspector__issues">
          <button
            v-for="issue in issues"
            :key="`${issue.nodeId}-${issue.field || issue.message}`"
            class="agent-inspector__issue"
            type="button"
            @click="handleIssueClick(issue)"
          >
            {{ issue.message }}
          </button>
        </div>

        <AgentSkillInspector
          v-if="state.selectedNodeId === 'skills'"
          :bindings="state.skillBindings"
          @add="emit('addSkills')"
          @move="handleMoveSkill"
          @remove="emit('removeSkill', $event)"
        />
        <template v-else-if="state.panelMode === 'base'">
          <ElTabs v-model="activeBaseTab" class="agent-inspector__tabs">
            <ElTabPane label="基础设置" name="basic">
              <AgentBaseForm
                :agent="state.agent"
                :categories="categories"
                :models="models"
                :tool-bindings="state.toolBindings"
                @change="emit('change')"
              />
            </ElTabPane>
            <ElTabPane label="对话体验" name="interaction">
              <AgentInteractionForm
                ref="interactionForm"
                :agent="state.agent"
                @change="emit('change')"
              />
            </ElTabPane>
            <ElTabPane label="内置工具" name="builtinTools">
              <AgentBuiltinToolsForm
                :agent="state.agent"
                :can-disable-shell-approval="canDisableShellApproval"
                @change="emit('change')"
              />
            </ElTabPane>
          </ElTabs>
        </template>
        <AgentKnowledgeForm
          v-else-if="selectedKnowledge"
          :binding="selectedKnowledge"
          :knowledges="knowledges"
          @change="emit('change')"
          @remove="emit('removeCapability')"
        />
        <AgentToolForm
          v-else-if="selectedTool"
          :binding="selectedTool"
          :kind="selectedToolKind"
          :mcp-tools-loading="
            Boolean(mcpToolsLoading[String(selectedTool.targetId || '')])
          "
          :options="selectedToolOptions"
          @change="emit('change')"
          @remove="emit('removeCapability')"
        />
        <div v-else class="agent-inspector__empty">
          <ElButton :icon="Close" text @click="emit('closeTryout')">
            返回基座
          </ElButton>
        </div>
      </div>
    </template>
  </aside>
</template>

<style scoped>
.agent-inspector {
  --agent-inspector-expanded-width: 960px;
  --agent-inspector-width: min(420px, calc(100vw - 320px));

  position: absolute;
  inset: var(--space-6) var(--space-6) 96px
    calc(100% - var(--space-6) - var(--agent-inspector-width));
  z-index: 20;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  background: var(--el-bg-color);
  background: color-mix(in srgb, var(--el-bg-color) 94%, transparent);
  border: 1px solid var(--el-border-color-lighter);
  border-radius: var(--radius-toolbar);
  box-shadow: var(--el-box-shadow-light);
  backdrop-filter: blur(16px);
}

.agent-inspector.is-tryout {
  transition:
    top var(--motion-duration-medium) var(--motion-ease-standard),
    right var(--motion-duration-medium) var(--motion-ease-standard),
    bottom var(--motion-duration-medium) var(--motion-ease-standard),
    left var(--motion-duration-medium) var(--motion-ease-standard),
    border-radius var(--motion-duration-medium) var(--motion-ease-standard),
    box-shadow var(--motion-duration-medium) var(--motion-ease-standard);
}

.agent-inspector.is-tryout-expanded {
  inset: var(--space-6)
    max(
      var(--space-6),
      calc((100% - var(--agent-inspector-expanded-width)) / 2)
    );
  z-index: 40;
  border-radius: var(--radius-panel);
  box-shadow: var(--shadow-float);
}

.agent-inspector__header {
  padding: var(--space-4);
  border-bottom: 1px solid var(--el-border-color-lighter);
}

.agent-inspector__body {
  flex: 1;
  min-height: 0;
  overflow: hidden auto;
  overscroll-behavior: contain;
}

.agent-inspector__title {
  font-size: 16px;
  font-weight: 600;
}

.agent-inspector__subtitle {
  margin-top: 4px;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.agent-inspector__issues {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 16px 0;
}

.agent-inspector__issue {
  padding: 8px 10px;
  font-size: 12px;
  color: var(--el-color-danger);
  text-align: left;
  cursor: pointer;
  background: var(--el-color-danger-light-9);
  border: 0;
  border-radius: 8px;
}

.agent-inspector__tabs :deep(.el-tabs__header) {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 0 var(--space-4);
  margin: 0;
  background: hsl(var(--surface-panel));
  border-bottom: 1px solid hsl(var(--line-subtle));
}

.agent-inspector__tabs :deep(.el-tabs__nav-wrap::after) {
  display: none;
}

.agent-inspector__empty {
  padding: 16px;
}

@media (max-width: 900px) {
  .agent-inspector {
    inset: calc(var(--space-8) + var(--space-8) + var(--space-2)) var(--space-4)
      96px;
  }

  .agent-inspector.is-tryout-expanded {
    inset: var(--space-4);
  }
}

@media (max-width: 480px) {
  .agent-inspector {
    inset: calc(var(--space-8) + var(--space-8) + var(--space-2)) var(--space-2)
      96px;
  }

  .agent-inspector.is-tryout-expanded {
    inset: var(--space-2);
  }

  .agent-inspector__header {
    padding: var(--space-3) var(--space-4);
  }

  .agent-inspector__issues {
    padding: var(--space-3) var(--space-3) 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .agent-inspector.is-tryout {
    transition: none;
  }
}
</style>
