<script setup lang="ts">
import { onMounted, onUnmounted, ref, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ArrowLeft, Bot, Loader2, AlertCircle, Activity, RefreshCw } from '@lucide/vue'
import { apiFetch } from '../services/api'
import type { AgentDetail } from '../types'
import ConfigTabs from '../components/config/ConfigTabs.vue'
import ConfigEditor from '../components/config/ConfigEditor.vue'
import { openDashboardLiveStream } from '../services/live'

const route = useRoute()
const router = useRouter()

const agent = ref<AgentDetail | null>(null)
const loading = ref(false)
const error = ref('')

const configFiles = ref<ConfigFileInfo[]>([])
const activeTab = ref(0)
const configsLoading = ref(false)
const configsError = ref('')
const activityItems = ref<AgentActivityItem[]>([])
const activityLoading = ref(false)
const activityError = ref('')
const summaryLoading = ref(false)
const summaryError = ref('')
const summary = ref<AgentSummary | null>(null)
const liveConnected = ref(false)
const liveUnavailable = ref(false)
let liveAbort: AbortController | null = null
let activityReloadTimer: ReturnType<typeof setTimeout> | null = null
let liveReconnectTimer: ReturnType<typeof setTimeout> | null = null
let liveStreamStopped = false
let lastLiveSequence = 0

const initLoading = ref(true)

interface EditorState {
  content: string
  savedContent: string
  saving: boolean
  dirty: boolean
  saveStatus: 'idle' | 'saved' | 'error'
  saveMessage: string
  backupStatus: string
  reloadStatus: string
  reloadMessage: string
}

interface ConfigFileInfo {
  fileName: string
  size: number
  modifiedAt: string
}

interface ConfigFileDetail extends ConfigFileInfo {
  content: string
}

interface AgentActivityItem {
  id: number | null
  type: string
  message: string
  at: string
  source: string
  relativeTime?: string | null
}

interface AgentSummary {
  now: AgentSummaryItem
  today: AgentSummaryItem
  generatedAt: string
}

interface AgentSummaryItem {
  text: string
  source: string
  timestamp?: string | null
}

interface SaveConfigResult {
  fileName: string
  size: number
  modifiedAt: string
  validation: {
    status: string
    fileKind: string
    errors: string[]
  }
  backup: {
    status: string
    backupCreated: boolean
  }
  reloadCheck: {
    status: string
    message: string
  }
}

const editorState = ref<EditorState>({
  content: '',
  savedContent: '',
  saving: false,
  dirty: false,
  saveStatus: 'idle',
  saveMessage: '',
  backupStatus: 'not_applicable',
  reloadStatus: 'not_supported',
  reloadMessage: '',
})

const agentId = route.params.id as string

const orderedTabs = ['IDENTITY.md', 'SOUL.md', 'AGENTS.md', 'TOOLS.md', 'HEARTBEAT.md', 'USER.md']

const currentFile = computed(() => {
  if (!configFiles.value.length) return null
  const activeFile = configFiles.value[activeTab.value]
  return activeFile || configFiles.value[0]
})

const activeTabFileName = computed(() => {
  return orderedTabs[activeTab.value] || null
})

const fallbackName = computed(() => {
  return agentId.charAt(0).toUpperCase() + agentId.slice(1)
})

function formatFileSize(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
}

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

const statusColor = (status: string): string => {
  switch (status) {
    case 'Online': return '#51d49a'
    case 'Degraded': return '#e5b05e'
    case 'Offline': return '#e16e75'
    default: return '#7e8799'
  }
}

function formatLastSeen(dateStr?: string): string {
  if (!dateStr) return 'N/A'
  const d = new Date(dateStr)
  return d.toLocaleDateString('de-DE', {
    year: 'numeric', month: 'short', day: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })
}

function formatActivityTime(item: AgentActivityItem): string {
  if (item.relativeTime) return item.relativeTime
  const d = new Date(item.at)
  return d.toLocaleDateString('de-DE', {
    month: 'short', day: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })
}

function activityTypeLabel(type: string): string {
  if (type === 'thinking') return 'Thinking'
  if (type === 'handoff') return 'Handoff'
  if (type === 'task') return 'Task'
  return 'Activity'
}

async function loadAgent() {
  loading.value = true
  error.value = ''
  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}`)
    if (!response.ok) throw new Error(`Agent "${agentId}" not found`)
    agent.value = await response.json()
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'Failed to load agent data'
  } finally {
    loading.value = false
  }
}

async function loadActivity() {
  activityLoading.value = true
  activityError.value = ''
  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}/activity`)
    if (!response.ok) throw new Error('Failed to load activity')
    activityItems.value = await response.json()
  } catch (e) {
    activityError.value = e instanceof Error ? e.message : 'Failed to load activity'
  } finally {
    activityLoading.value = false
  }
}

async function loadSummary() {
  summaryLoading.value = true
  summaryError.value = ''
  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}/summary`)
    if (!response.ok) throw new Error('Failed to load summary')
    summary.value = await response.json()
  } catch (e) {
    summaryError.value = e instanceof Error ? e.message : 'Failed to load summary'
  } finally {
    summaryLoading.value = false
  }
}

function scheduleActivityReload() {
  if (activityReloadTimer) return
  activityReloadTimer = setTimeout(async () => {
    activityReloadTimer = null
    await loadActivity()
    await loadSummary()
  }, 250)
}

function formatSummaryTimestamp(value?: string | null): string {
  if (!value) return 'No timestamp'
  const d = new Date(value)
  return d.toLocaleDateString('de-DE', {
    month: 'short', day: 'numeric',
    hour: '2-digit', minute: '2-digit',
  })
}

function summarySourceLabel(source: string): string {
  switch (source) {
    case 'nexus-activity': return 'Nexus activity'
    case 'gateway-session-history': return 'Gateway history'
    case 'derived-mixed': return 'Derived from mixed feed'
    case 'none': return 'No data'
    default: return source
  }
}

function scheduleStreamReconnect() {
  if (liveStreamStopped || liveReconnectTimer) return
  liveReconnectTimer = setTimeout(() => {
    liveReconnectTimer = null
    void connectActivityStream()
  }, 1500)
}

async function connectActivityStream() {
  liveAbort?.abort()
  liveAbort = new AbortController()

  try {
    const stream = await openDashboardLiveStream((event, data) => {
      const cursor = (data as any)?.cursor
      if (typeof cursor?.sequence === 'number') lastLiveSequence = cursor.sequence

      if (event === 'snapshot') {
        liveConnected.value = true
        liveUnavailable.value = false
        return
      }

      if (event !== 'update') return
      const envelope = (data as any)?.envelope
      if (envelope?.type !== 'activity.created') return

      const agentIds = Array.isArray(envelope?.payload?.agentIds)
        ? envelope.payload.agentIds.map((id: unknown) => String(id).toLowerCase())
        : []

      if (agentIds.includes(agentId.toLowerCase())) {
        scheduleActivityReload()
      }
    }, { signal: liveAbort.signal, afterSequence: lastLiveSequence || null })

    await stream.closed
    if (!liveStreamStopped) {
      liveConnected.value = false
      scheduleStreamReconnect()
    }
  } catch {
    liveConnected.value = false
    liveUnavailable.value = true
    if (!liveStreamStopped) scheduleStreamReconnect()
  }
}

async function loadConfigFiles() {
  configsLoading.value = true
  configsError.value = ''
  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}/config`)
    if (!response.ok) throw new Error('Failed to load config files')
    configFiles.value = await response.json()
    if (configFiles.value.length > 0) {
      const fileName = configFiles.value[0].fileName
      const tabIndex = orderedTabs.indexOf(fileName)
      activeTab.value = tabIndex >= 0 ? tabIndex : 0
    }
    if (configFiles.value.length > 0) {
      await loadFileContent(configFiles.value[0].fileName)
    }
  } catch (e) {
    configsError.value = e instanceof Error ? e.message : 'Failed to load config files'
  } finally {
    configsLoading.value = false
  }
}

async function loadFileContent(fileName: string) {
  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}/config/${encodeURIComponent(fileName)}`)
    if (!response.ok) throw new Error(`Failed to load ${fileName}`)
    const data: ConfigFileDetail = await response.json()
    editorState.value = {
      content: data.content,
      savedContent: data.content,
      saving: false,
      dirty: false,
      saveStatus: 'idle',
      saveMessage: '',
      backupStatus: 'not_applicable',
      reloadStatus: 'not_supported',
      reloadMessage: '',
    }
  } catch (e) {
    editorState.value = {
      content: '',
      savedContent: '',
      saving: false,
      dirty: false,
      saveStatus: 'error',
      saveMessage: e instanceof Error ? e.message : `Failed to load ${fileName}`,
      backupStatus: 'not_applicable',
      reloadStatus: 'not_supported',
      reloadMessage: '',
    }
  }
}

async function switchTab(index: number) {
  if (activeTab.value === index) return
  activeTab.value = index
  const fileName = orderedTabs[index]
  if (!fileName) return
  await loadFileContent(fileName)
}

function onContentChange(value: string) {
  editorState.value.content = value
  editorState.value.dirty = value !== editorState.value.savedContent
}

async function saveFile() {
  const fileName = activeTabFileName.value
  if (!fileName || !editorState.value.dirty) return

  editorState.value.saving = true
  editorState.value.saveStatus = 'idle'
  editorState.value.saveMessage = ''
  editorState.value.backupStatus = 'not_applicable'
  editorState.value.reloadStatus = 'not_supported'
  editorState.value.reloadMessage = ''

  try {
    const response = await apiFetch(`/api/v1/agents/${agentId}/config/${encodeURIComponent(fileName)}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ content: editorState.value.content }),
    })

    if (!response.ok) {
      const err = await response.json().catch(() => ({}))
      const problem = err as { error?: string; errors?: Record<string, string[]> }
      const detail = problem.error
        || Object.values(problem.errors ?? {}).flat().join(' ')
        || 'Failed to save file'
      throw new Error(detail)
    }

    const result: SaveConfigResult = await response.json()
    editorState.value.savedContent = editorState.value.content
    editorState.value.dirty = false
    editorState.value.saveStatus = 'saved'
    editorState.value.saveMessage = `Gespeichert · Backup ${result.backup.status}`
    editorState.value.backupStatus = result.backup.status
    editorState.value.reloadStatus = result.reloadCheck.status
    editorState.value.reloadMessage = result.reloadCheck.message

    const idx = configFiles.value.findIndex(f => f.fileName === fileName)
    if (idx >= 0) {
      configFiles.value[idx] = { ...configFiles.value[idx], size: result.size, modifiedAt: result.modifiedAt }
    }

    setTimeout(() => {
      if (editorState.value.saveStatus === 'saved') {
        editorState.value.saveStatus = 'idle'
        editorState.value.saveMessage = ''
      }
    }, 2000)
  } catch (e) {
    editorState.value.saveStatus = 'error'
    editorState.value.saveMessage = e instanceof Error ? e.message : 'Failed to save file'
    editorState.value.backupStatus = 'not_applicable'
    editorState.value.reloadStatus = 'not_supported'
  } finally {
    editorState.value.saving = false
  }
}

onMounted(async () => {
  liveStreamStopped = false
  initLoading.value = true
  await Promise.allSettled([
    loadAgent(),
    loadConfigFiles(),
    loadActivity(),
    loadSummary(),
  ])
  connectActivityStream()
  initLoading.value = false
})

onUnmounted(() => {
  liveStreamStopped = true
  liveAbort?.abort()
  liveAbort = null
  if (activityReloadTimer) clearTimeout(activityReloadTimer)
  if (liveReconnectTimer) clearTimeout(liveReconnectTimer)
})
</script>

<template>
  <div class="detail-page">
    <button class="back-link" @click="router.push('/team')">
      <ArrowLeft :size="14" />
      Zurück zum Team
    </button>

    <div v-if="initLoading" class="status-message">
      <Loader2 :size="20" class="spin" />
      Loading agent data...
    </div>

    <template v-else>
      <!-- Agent header -->
      <div class="agent-header">
        <div class="agent-avatar" :class="agentId">
          <Bot :size="24" />
        </div>
        <div class="agent-header-info">
          <span class="eyebrow">{{ agent?.role?.toUpperCase() || 'AGENT' }}</span>
          <h1>{{ agent?.name || fallbackName }}</h1>
          <div v-if="agent" class="agent-status-row">
            <span :style="{ background: statusColor(agent.status) }" class="status-dot"></span>
            <span class="status-label">{{ agent.status }}</span>
            <span class="status-sep">·</span>
            <span class="status-label mono">{{ agent.model || 'N/A' }}</span>
            <span v-if="agent.lastSeen" class="status-sep">·</span>
            <span v-if="agent.lastSeen" class="status-label">
              <Activity :size="11" />
              {{ formatLastSeen(agent.lastSeen) }}
            </span>
          </div>
          <div v-if="error && !agent" class="agent-status-row">
            <span class="status-label muted">
              <AlertCircle :size="11" />
              {{ error }}
            </span>
          </div>
        </div>
      </div>

      <section class="thinking-section">
        <header class="section-head">
          <div>
            <span class="eyebrow">LIVE</span>
            <h2>Thinking <span :class="['live-dot', { on: liveConnected }]"></span></h2>
            <p class="section-note">
              Nexus activity streams live. Gateway session history remains read-only fallback and refreshes when related Nexus events arrive or on manual reload.
            </p>
          </div>
          <button class="icon-button" :disabled="activityLoading || summaryLoading" @click="Promise.allSettled([loadActivity(), loadSummary()])">
            <RefreshCw :size="14" :class="{ spin: activityLoading }" />
          </button>
        </header>

        <div v-if="summaryLoading && !summary" class="status-message compact">
          <Loader2 :size="16" class="spin" />
          Loading summaries...
        </div>

        <div v-else-if="summaryError && !summary" class="status-message compact error">
          <AlertCircle :size="16" />
          {{ summaryError }}
        </div>

        <div v-else-if="summary" class="summary-row">
          <div class="summary-card">
            <span>Now</span>
            <p>{{ summary.now.text }}</p>
            <small>{{ summarySourceLabel(summary.now.source) }} · {{ formatSummaryTimestamp(summary.now.timestamp) }}</small>
          </div>
          <div class="summary-card">
            <span>Today</span>
            <p>{{ summary.today.text }}</p>
            <small>{{ summarySourceLabel(summary.today.source) }} · {{ formatSummaryTimestamp(summary.today.timestamp) }}</small>
          </div>
        </div>

        <div v-else class="status-message compact">
          No summary available.
        </div>

        <div v-if="summaryError && summary" class="status-message compact error summary-inline-error">
          <AlertCircle :size="14" />
          {{ summaryError }}
        </div>

        <div v-if="liveUnavailable" class="status-message compact">
          Live stream reconnecting…
        </div>

        <div v-if="activityLoading && !activityItems.length" class="status-message compact">
          <Loader2 :size="16" class="spin" />
          Loading activity...
        </div>

        <div v-else-if="activityError" class="status-message compact error">
          <AlertCircle :size="16" />
          {{ activityError }}
        </div>

        <div v-else-if="activityItems.length" class="thinking-list">
          <article
            v-for="item in activityItems"
            :key="`${item.source}-${item.id ?? item.at}-${item.message}`"
            class="thinking-item"
          >
            <div class="thinking-meta">
              <span class="type-pill">{{ activityTypeLabel(item.type) }}</span>
              <span>{{ formatActivityTime(item) }}</span>
            </div>
            <p>{{ item.message }}</p>
          </article>
        </div>

        <div v-else class="status-message compact">
          No recent activity.
        </div>
      </section>

      <!-- Config section -->
      <div class="config-section">
        <div v-if="configsLoading" class="status-message">
          <Loader2 :size="16" class="spin" />
          Loading config files...
        </div>

        <div v-else-if="configsError" class="status-message error">
          <AlertCircle :size="16" />
          {{ configsError }}
        </div>

        <template v-else>
          <ConfigTabs
            :tabs="orderedTabs"
            :active-tab="activeTab"
            @switch-tab="switchTab"
          />

          <ConfigEditor
            :file-name="activeTabFileName"
            :file-size="currentFile ? formatFileSize(currentFile.size) : ''"
            :file-modified="currentFile ? formatModifiedAt(currentFile.modifiedAt) : ''"
            :content="editorState.content"
            :dirty="editorState.dirty"
            :saving="editorState.saving"
            :save-status="editorState.saveStatus"
            :save-message="editorState.saveMessage"
            :backup-status="editorState.backupStatus"
            :reload-status="editorState.reloadStatus"
            :reload-message="editorState.reloadMessage"
            @update-content="onContentChange"
            @save="saveFile"
          />
        </template>
      </div>
    </template>
  </div>
</template>

<style scoped>
.detail-page {
  max-width: 960px;
  margin: 0 auto;
}

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: var(--panel);
  color: #7e8799;
  font-size: 10.5px;
  cursor: pointer;
  margin-bottom: 20px;
  transition: border-color 0.15s, color 0.15s;
}
.back-link:hover {
  border-color: #443d7c;
  color: #d8dbe3;
}

.status-message {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 48px;
  color: #7e8799;
  font-size: 12px;
}
.status-message.error {
  color: #e16e75;
}
.status-message.compact {
  padding: 20px;
}
.spin {
  animation: spin 1s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}

.agent-header {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
  padding-bottom: 20px;
  border-bottom: 1px solid var(--line);
}
.agent-avatar {
  width: 52px;
  height: 52px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  background: rgba(139,124,246,.1);
  color: #8b7cf6;
  flex-shrink: 0;
}
.agent-avatar.iris { background: rgba(139,124,246,.15); color: #8b7cf6; }
.agent-avatar.programmer { background: rgba(77,140,246,.15); color: #4d8cf6; }
.agent-avatar.architekt { background: rgba(77,168,246,.15); color: #4da8f6; }
.agent-avatar.reviewer { background: rgba(246,168,77,.15); color: #f6a84d; }
.agent-avatar.researcher { background: rgba(139,77,246,.15); color: #8b4df6; }
.agent-avatar.executor { background: rgba(77,246,212,.15); color: #4df6d4; }

.agent-header-info {
  flex: 1;
}
.agent-header-info .eyebrow {
  display: block;
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--accent, #7b6ef2);
  text-transform: uppercase;
  margin-bottom: 2px;
}
.agent-header-info h1 {
  margin: 0 0 4px;
  font-size: 20px;
  font-weight: 600;
  color: #e8eaf0;
}
.agent-status-row {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
}
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}
.status-label {
  font-size: 11px;
  color: #7e8799;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.status-label.muted { color: #6b7385; }
.status-label.mono { font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace; }
.status-sep { color: #3d4152; font-size: 11px; }

.thinking-section {
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel);
  margin-bottom: 16px;
  overflow: hidden;
}
.section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--line);
}
.section-head .eyebrow {
  display: block;
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .12em;
  color: var(--accent, #7b6ef2);
}
.section-head h2 {
  margin: 2px 0 0;
  color: #e8eaf0;
  font-size: 13px;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.section-note {
  margin: 6px 0 0;
  color: #6f788b;
  font-size: 10px;
  line-height: 1.45;
  max-width: 560px;
}
.live-dot {
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background: #6b7385;
}
.live-dot.on {
  background: #51d49a;
}
.icon-button {
  width: 30px;
  height: 30px;
  border: 1px solid var(--line);
  border-radius: 7px;
  background: rgba(255,255,255,.03);
  color: #9ba3b5;
  display: grid;
  place-items: center;
  cursor: pointer;
}
.icon-button:disabled {
  opacity: .65;
  cursor: default;
}
.thinking-list {
  display: grid;
}
.summary-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: rgba(255,255,255,.05);
  border-bottom: 1px solid var(--line);
}
.summary-row > div {
  background: var(--panel);
  padding: 12px 16px;
}
.summary-card small {
  display: block;
  margin-top: 7px;
  color: #6f788b;
  font-size: 9.5px;
  line-height: 1.4;
}
.summary-row span {
  display: block;
  color: #6f788b;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  margin-bottom: 5px;
}
.summary-row p {
  margin: 0;
  color: #cbd0dc;
  font-size: 11px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.thinking-item {
  padding: 12px 16px;
  border-bottom: 1px solid rgba(255,255,255,.05);
}
.thinking-item:last-child {
  border-bottom: 0;
}
.thinking-meta {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
  color: #6f788b;
  font-size: 10px;
}
.type-pill {
  padding: 2px 6px;
  border-radius: 999px;
  border: 1px solid rgba(123,110,242,.24);
  color: #aaa1ff;
  background: rgba(123,110,242,.08);
  font-size: 9px;
}
.thinking-item p {
  margin: 0;
  color: #cbd0dc;
  font-size: 11px;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.summary-inline-error {
  border-bottom: 1px solid var(--line);
}

@media (max-width: 640px) {
  .detail-page {
    max-width: 100%;
  }
  .summary-row {
    grid-template-columns: 1fr;
  }
}
</style>
