<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue'
import { FileText, Search, ArrowLeft, Clock, Database } from '@lucide/vue'
import { RouterLink } from 'vue-router'
import {
  useMemoryFileQuery,
  useMemoryFilesQuery,
  useMemorySearchQuery,
} from '../api/knowledge'
import { renderMarkdown } from '../utils/markdown'
import AsyncStatePanel from '../components/mission-control/AsyncStatePanel.vue'

// State
const memoriesQuery = useMemoryFilesQuery()
const memories = computed(() => memoriesQuery.data.value ?? [])
const loading = computed(() => memoriesQuery.isPending.value)

const searchQuery = ref('')
const debouncedSearch = ref('')
const searchResultsQuery = useMemorySearchQuery(debouncedSearch)
const searchResults = computed(() => searchResultsQuery.data.value ?? [])
const searchLoading = computed(() => searchResultsQuery.isFetching.value)
const searchDebounce = ref<ReturnType<typeof setTimeout> | null>(null)

const selectedMemoryName = ref('')
const memoryQuery = useMemoryFileQuery(selectedMemoryName)
const selectedMemory = computed(() =>
  selectedMemoryName.value ? memoryQuery.data.value ?? null : null,
)
const contentLoading = computed(() => memoryQuery.isFetching.value)
const listProblem = computed(() => memoriesQuery.error.value)
const contentProblem = computed(() => memoryQuery.error.value)
const searchProblem = computed(() => searchResultsQuery.error.value)

// Sorted memories (newest first)
const sortedMemories = computed(() => {
  return [...memories.value].sort((a, b) => new Date(b.modifiedAt).getTime() - new Date(a.modifiedAt).getTime())
})

function onSearchInput() {
  if (searchDebounce.value) clearTimeout(searchDebounce.value)
  const q = searchQuery.value.trim()
  if (q.length < 2) {
    debouncedSearch.value = ''
    return
  }
  searchDebounce.value = setTimeout(() => {
    debouncedSearch.value = q
  }, 300)
}

function selectMemory(name: string) {
  debouncedSearch.value = ''
  searchQuery.value = ''
  selectedMemoryName.value = name
}

function loadMemoryContent(name: string) {
  selectedMemoryName.value = name
}

function goBack() {
  selectedMemoryName.value = ''
}

function retryList() {
  void memoriesQuery.refetch()
}

function retrySearch() {
  void searchResultsQuery.refetch()
}

function retryContent() {
  void memoryQuery.refetch()
}

function formatDate(dateStr: string): string {
  return new Date(dateStr).toLocaleDateString('de-DE', {
    year: 'numeric', month: 'short', day: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })
}

function formatSize(bytes: number | string): string {
  const numericBytes = Number(bytes)
  if (!Number.isFinite(numericBytes)) return String(bytes)
  if (numericBytes < 1024) return `${numericBytes} B`
  if (numericBytes < 1024 * 1024) return `${(numericBytes / 1024).toFixed(1)} KB`
  return `${(numericBytes / (1024 * 1024)).toFixed(1)} MB`
}

onUnmounted(() => {
  if (searchDebounce.value) clearTimeout(searchDebounce.value)
})
</script>

<template>
  <div class="memory-page nexus-page">
  <div class="page-heading">
    <div>
      <span class="eyebrow">LIFECYCLE</span>
      <h1>Memory</h1>
      <p>Browse agent memory files stored across workspaces.</p>
    </div>
  </div>

  <div class="memory-search-bar">
    <Search :size="16" />
    <input
      v-model="searchQuery"
      aria-label="Search memory files"
      placeholder="Search memory files..."
      @input="onSearchInput"
    />
    <kbd v-if="!searchQuery">Type at least 2 characters</kbd>
  </div>

  <div class="memory-layout">
    <!-- Left column: file list or search results -->
    <aside class="memory-sidebar">
      <!-- Search results -->
      <template v-if="searchQuery.trim().length >= 2">
        <AsyncStatePanel
          v-if="searchLoading && !searchResults.length"
          state="loading"
          title="Memory wird durchsucht"
          compact
        />
        <AsyncStatePanel
          v-else-if="searchProblem && !searchResults.length"
          state="error"
          title="Suche fehlgeschlagen"
          :problem="searchProblem"
          compact
          @action="retrySearch"
        />
        <template v-else-if="searchResults.length">
          <AsyncStatePanel
            v-if="searchProblem"
            state="stale"
            title="Suchergebnisse möglicherweise veraltet"
            :problem="searchProblem"
            action-label="Erneut suchen"
            compact
            @action="retrySearch"
          />
          <div class="memory-list-header">Search results ({{ searchResults.length }})</div>
          <button
            v-for="result in searchResults"
            :key="result.path"
            type="button"
            class="memory-file-item"
            @click="selectMemory(result.name)"
          >
            <div class="memory-file-icon"><FileText :size="14" /></div>
            <div class="memory-file-info">
              <strong>{{ result.name }}</strong>
              <span class="memory-file-excerpt">{{ result.excerpt }}</span>
              <span class="memory-file-meta">{{ result.sourceAgentId }} · {{ result.workspacePath }}</span>
            </div>
          </button>
        </template>
        <AsyncStatePanel
          v-else
          state="empty"
          title="Keine Treffer"
          message="Passe den Suchbegriff an oder öffne die vollständige Dateiliste."
          compact
        />
      </template>

      <!-- File list (default) -->
      <template v-else>
        <AsyncStatePanel
          v-if="loading && !memories.length"
          state="loading"
          title="Memory-Dateien werden geladen"
          compact
        />
        <AsyncStatePanel
          v-else-if="listProblem && !memories.length"
          state="error"
          title="Memory-Dateien nicht verfügbar"
          :problem="listProblem"
          compact
          @action="retryList"
        />
        <template v-else-if="sortedMemories.length">
          <AsyncStatePanel
            v-if="listProblem"
            state="stale"
            title="Dateiliste möglicherweise veraltet"
            :problem="listProblem"
            action-label="Aktualisieren"
            compact
            @action="retryList"
          />
          <div class="memory-list-header">{{ sortedMemories.length }} files</div>
          <button
            v-for="mem in sortedMemories"
            :key="mem.name"
            type="button"
            :class="['memory-file-item', { active: selectedMemory?.name === mem.name }]"
            :aria-current="selectedMemory?.name === mem.name ? 'true' : undefined"
            @click="loadMemoryContent(mem.name)"
          >
            <div class="memory-file-icon"><FileText :size="14" /></div>
            <div class="memory-file-info">
              <strong>{{ mem.name }}</strong>
              <span class="memory-file-meta">
                <Clock :size="10" /> {{ formatDate(mem.modifiedAt) }}
                · {{ formatSize(mem.size) }}
              </span>
              <span class="memory-file-meta">{{ mem.sourceAgentId }} · {{ mem.workspacePath }}</span>
            </div>
          </button>
        </template>
        <AsyncStatePanel
          v-else
          state="empty"
          title="Keine Memory-Dateien"
          message="OpenClaw meldet für die verbundenen Agent-Workspaces noch keine Memory-Dateien."
          compact
        />
      </template>
    </aside>

    <!-- Right column: content -->
    <main class="memory-content">
      <AsyncStatePanel
        v-if="contentLoading && !selectedMemory"
        state="loading"
        title="Memory-Inhalt wird geladen"
      />
      <AsyncStatePanel
        v-else-if="contentProblem && !selectedMemory"
        state="error"
        title="Memory-Inhalt nicht verfügbar"
        :problem="contentProblem"
        @action="retryContent"
      />
      <template v-else-if="selectedMemory">
        <AsyncStatePanel
          v-if="contentProblem"
          state="stale"
          title="Angezeigter Inhalt ist möglicherweise veraltet"
          :problem="contentProblem"
          action-label="Aktualisieren"
          compact
          @action="retryContent"
        />
        <header class="memory-content-header">
          <button type="button" class="memory-back-btn" @click="goBack">
            <ArrowLeft :size="14" />
            Back
          </button>
          <div>
            <strong>{{ selectedMemory.name }}</strong>
            <span class="memory-content-meta">
              <Clock :size="10" /> {{ formatDate(selectedMemory.modifiedAt) }}
              · {{ formatSize(selectedMemory.size) }}
            </span>
            <span class="source-context">
              Agent
              <RouterLink :to="{ name: 'AgentDetail', params: { id: selectedMemory.sourceAgentId } }">
                {{ selectedMemory.sourceAgentId }}
              </RouterLink>
              · <code>{{ selectedMemory.workspacePath }}</code>
            </span>
          </div>
        </header>
        <article
          class="memory-rendered"
          v-html="renderMarkdown(selectedMemory.content)"
        ></article>
      </template>
      <template v-else>
        <div class="memory-empty-state">
          <Database :size="28" />
          <h3>Select a memory file</h3>
          <p>Choose a file from the list or search to view its contents.</p>
        </div>
      </template>
    </main>
  </div>
  </div>
</template>

<style scoped>
.memory-search-bar {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 10px 14px;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--panel);
  margin-bottom: 16px;
  color: var(--tx-3);
}
.memory-search-bar input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  color: var(--tx);
  font: inherit;
  font-size: 13px;
}
.memory-search-bar kbd {
  padding: 2px 5px;
  border: 1px solid var(--space-4);
  border-radius: 4px;
  color: var(--tx-3);
  font-size: 9px;
}
.memory-layout {
  display: grid;
  grid-template-columns: 320px 1fr;
  gap: 12px;
  min-height: 480px;
}
.memory-sidebar {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  padding: 8px;
  max-height: 640px;
  overflow-y: auto;
}
.memory-list-header {
  font-size: 9px;
  font-weight: 700;
  color: var(--tx-3);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  padding: 10px 8px 6px;
}
.memory-file-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 10px;
  border: none;
  border-radius: 7px;
  background: transparent;
  color: var(--tx);
  text-align: left;
  cursor: pointer;
  font: inherit;
}
.memory-file-item:hover,
.memory-file-item.active {
  background: var(--accent-soft);
}
.memory-file-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  border-radius: 6px;
  color: var(--tx-2);
  background: color-mix(in srgb, var(--a-mid) 10%, transparent);
}
.memory-file-info strong {
  display: block;
  font-size: 11px;
  margin-bottom: 3px;
  word-break: break-word;
}
.memory-file-meta,
.memory-file-excerpt {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 9px;
  color: var(--tx-3);
  line-height: 1.5;
}
.memory-file-excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.memory-content {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  padding: 24px;
  min-height: 480px;
  overflow-y: auto;
}
.memory-content-header {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: 20px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--line);
}
.memory-content-header > div strong {
  display: block;
  font-size: 13px;
  margin-bottom: 3px;
}
.memory-content-meta {
  display: flex;
  align-items: center;
  gap: 4px;
  font-size: 9px;
  color: var(--tx-3);
}
.source-context {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 5px;
  color: var(--tx-3);
  font-family: var(--font-mono-v2);
  font-size: 10px;
}
.source-context a {
  color: var(--a-blue);
}
.source-context code {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.memory-back-btn {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px 10px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: transparent;
  color: var(--tx-3);
  font-size: 10px;
  cursor: pointer;
  flex-shrink: 0;
}
.memory-back-btn:hover {
  color: var(--tx);
  background: var(--accent-soft);
}
.memory-status {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px;
  color: var(--tx-3);
  font-size: 11px;
}
.memory-status.error {
  color: var(--st-block);
}
.memory-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 360px;
  color: var(--tx-3);
}
.memory-empty-state h3 {
  margin: 12px 0 6px;
  font-size: 14px;
  color: var(--tx-2);
}
.memory-empty-state p {
  margin: 0;
  font-size: 10px;
  color: var(--tx-3);
}
.memory-rendered {
  font-size: 12px;
  line-height: 1.7;
  color: var(--tx);
}
.memory-rendered :deep(h1),
.memory-rendered :deep(h2),
.memory-rendered :deep(h3) {
  color: var(--tx);
  margin: 1.2em 0 0.5em;
}
.memory-rendered :deep(h1) { font-size: 1.3rem; }
.memory-rendered :deep(h2) { font-size: 1.1rem; }
.memory-rendered :deep(h3) { font-size: 1rem; }
.memory-rendered :deep(p) { margin: 0.6em 0; }
.memory-rendered :deep(code) {
  padding: 2px 5px;
  background: color-mix(in srgb, var(--a-mid) 8%, transparent);
  border-radius: 4px;
  font-size: 11px;
}
.memory-rendered :deep(pre) {
  padding: 12px;
  border-radius: 8px;
  background: var(--space-1);
  border: 1px solid var(--nx-line);
  overflow-x: auto;
}
.memory-rendered :deep(pre code) {
  background: none;
  padding: 0;
}
.memory-rendered :deep(a) {
  color: var(--tx-2);
  text-decoration: none;
}
.memory-rendered :deep(a:hover) {
  text-decoration: underline;
}
.memory-rendered :deep(ul) {
  padding-left: 20px;
}
.memory-rendered :deep(li) {
  margin: 0.3em 0;
}
.memory-rendered :deep(hr) {
  border: none;
  border-top: 1px solid var(--nx-line);
  margin: 1.2em 0;
}
.memory-rendered :deep(strong) {
  color: var(--tx);
}
.spin {
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (max-width: 900px) {
  .memory-layout {
    grid-template-columns: 1fr;
  }
  .memory-sidebar {
    max-height: 280px;
  }
}
</style>
