<script setup lang="ts">
import type { AuditEvent } from '~/types/audit'

// Local UI state for filters. Drives the params we pass to /api/audit.
const search = ref('')
const actionFilter = ref('')        // exact or prefix (e.g. 'tenant')
const outcomeFilter = ref<'' | 'success' | 'failure'>('')
const tenantFilter = ref('')

const params = computed<Record<string, string>>(() => {
  const p: Record<string, string> = {}
  if (search.value.trim()) p.q = search.value.trim()
  if (actionFilter.value) p.action = actionFilter.value
  if (outcomeFilter.value) p.outcome = outcomeFilter.value
  if (tenantFilter.value.trim()) p.tenantSlug = tenantFilter.value.trim()
  return p
})

const { data: events, pending, refresh } = await useFetch<AuditEvent[]>('/api/audit', {
  default: () => [],
  query: params,
})

// Pagination via the `before` cursor — request the next page using the
// timestamp of the last event we have.
const olderPages = ref<AuditEvent[]>([])
const reachedEnd = ref(false)

const allEvents = computed(() => [...(events.value ?? []), ...olderPages.value])

// Reset older-page state whenever the live filter set changes; otherwise
// older results from a previous filter combination linger.
watch(params, () => {
  olderPages.value = []
  reachedEnd.value = false
})

async function loadMore() {
  const last = allEvents.value[allEvents.value.length - 1]
  if (!last) return
  const next = await $fetch<AuditEvent[]>('/api/audit', {
    query: { ...params.value, before: last.at },
  })
  if (!next.length) {
    reachedEnd.value = true
    return
  }
  olderPages.value.push(...next)
}

// Quick-pick filter chips that map to common queries.
const QUICK_ACTIONS = [
  { label: 'All actions',  value: '' },
  { label: 'Tenants',      value: 'tenant' },
  { label: 'Partners',     value: 'partner' },
  { label: 'Flags',        value: 'flag' },
  { label: 'Users',        value: 'user' },
]

function fmtAbs(iso: string) {
  const d = new Date(iso)
  return d.toLocaleString('da-DK', { dateStyle: 'short', timeStyle: 'medium' })
}
function tone(e: AuditEvent): 'info' | 'warn' | 'bad' | 'ok' {
  if (e.outcome === 'failure') return 'bad'
  if (e.action.includes('suspended') || e.action.includes('killed') || e.action.includes('deleted') || e.action.includes('terminated')) return 'warn'
  return 'info'
}
function shortIp(ip?: string) {
  if (!ip) return '—'
  // Strip v4-in-v6 prefix that node sockets sometimes report (::ffff:1.2.3.4 → 1.2.3.4)
  return ip.replace(/^::ffff:/, '')
}
</script>

<template>
  <div>
    <PageHeader
      eyebrow="Compliance"
      title="Global audit log"
      :subtitle="`${allEvents.length} event${allEvents.length === 1 ? '' : 's'} · every privileged action recorded by platform-api`"
    >
      <template #actions>
        <UiButton variant="secondary" :disabled="pending" @click="refresh()">
          <template #leading><UiIcon name="chevDown" :size="13" /></template>
          Refresh
        </UiButton>
      </template>
    </PageHeader>

    <div class="stage">
      <div class="toolbar">
        <div class="search">
          <UiIcon name="search" :size="13" />
          <input v-model="search" placeholder="action, actor, target, tenant…" type="text" />
        </div>
        <div class="chips">
          <button
            v-for="q in QUICK_ACTIONS"
            :key="q.label"
            :class="['chip', { on: actionFilter === q.value }]"
            type="button"
            @click="actionFilter = q.value"
          >{{ q.label }}</button>
        </div>
        <div class="chips">
          <button :class="['chip', { on: outcomeFilter === '' }]" type="button" @click="outcomeFilter = ''">Any</button>
          <button :class="['chip', { on: outcomeFilter === 'success' }]" type="button" @click="outcomeFilter = 'success'">Success</button>
          <button :class="['chip', { on: outcomeFilter === 'failure' }]" type="button" @click="outcomeFilter = 'failure'">Failure</button>
        </div>
        <Mono dim class="streaming">
          <StatusDot color="var(--ok)" :size="6" :glow="false" />
          live · backed by Mongo
        </Mono>
      </div>

      <Card :pad="0">
        <table v-if="allEvents.length">
          <thead>
            <tr>
              <th>Time</th>
              <th>Actor</th>
              <th>Action</th>
              <th>Target</th>
              <th>Tenant</th>
              <th>IP</th>
              <th class="r">Result</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="e in allEvents" :key="e._id">
              <td><Mono>{{ fmtAbs(e.at) }}</Mono></td>
              <td class="actor">
                <div v-if="e.actorType === 'system'" class="sys">sys</div>
                <Avatar v-else :name="e.actorEmail || '?'" :size="22" />
                <div>
                  <div class="name">{{ e.actorEmail || 'system' }}</div>
                  <Mono dim>{{ e.source }}</Mono>
                </div>
              </td>
              <td><Mono class="action">{{ e.action }}</Mono></td>
              <td><span class="target">{{ e.resourceName || e.resourceId || '—' }}</span></td>
              <td>
                <Mono v-if="e.tenantSlug">{{ e.tenantSlug }}</Mono>
                <Mono v-else dim>—</Mono>
              </td>
              <td><Mono dim>{{ shortIp(e.actorIp) }}</Mono></td>
              <td class="r">
                <Badge :tone="tone(e)" dot>
                  {{ e.outcome === 'failure' ? 'fail' : e.outcome === 'success' ? 'ok' : '—' }}
                </Badge>
              </td>
            </tr>
          </tbody>
        </table>
        <div v-else class="empty"><Mono dim>// no events match the current filters</Mono></div>
      </Card>

      <div v-if="allEvents.length" class="footer">
        <UiButton v-if="!reachedEnd" variant="secondary" :disabled="pending" @click="loadMore">
          Load older events
        </UiButton>
        <Mono v-else dim>// reached the start of the log</Mono>
      </div>

      <Mono dim class="note">
        // sourced from /audit on platform-api · append-only · hash-chain tamper
        evidence + external system ingest (Authentik / OCIS / Stalwart) are
        planned follow-ups (see docs/NEXT-STEPS.md)
      </Mono>
    </div>
  </div>
</template>

<style scoped>
.stage { padding: 24px 40px 64px 40px; display: flex; flex-direction: column; gap: 16px; }

.toolbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.search {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text-mute);
  min-width: 320px;
}
.search input {
  background: transparent;
  border: 0;
  outline: 0;
  flex: 1;
  font-family: inherit;
  font-size: 12px;
  color: var(--text);
}

.chips { display: flex; gap: 6px; flex-wrap: wrap; }
.chip {
  appearance: none;
  padding: 6px 10px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-dim);
  border-radius: 999px;
  font-family: inherit;
  font-size: 12px;
  cursor: pointer;
}
.chip:hover { color: var(--text); }
.chip.on { background: var(--text); color: var(--bg); border-color: var(--text); }

.streaming { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; }

table { width: 100%; border-collapse: collapse; }
th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 9px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  padding: 12px 16px;
  font-weight: 500;
  border-bottom: 1px solid var(--border);
}
th.r, td.r { text-align: right; }
td { padding: 10px 16px; font-size: 12px; border-top: 1px solid var(--border); vertical-align: middle; }
td.actor { display: flex; align-items: center; gap: 10px; }
.name { font-size: 12px; font-weight: 500; }
.action { font-weight: 500; }
.target { font-size: 12px; color: var(--text-dim); }
.sys {
  width: 22px; height: 22px;
  border-radius: 4px;
  background: var(--text);
  color: var(--bg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 9px;
  font-family: var(--font-mono);
}

.empty { padding: 40px 20px; text-align: center; }
.footer { display: flex; justify-content: center; padding: 4px 0; }
.note { display: block; padding: 4px 4px 0 4px; }
</style>
