<script setup lang="ts">
// Tier 2 Block G — security controls for one event.
//
// Three concerns share this card because they're conceptually one
// surface ("tune the fraud detector for this event"):
//   1. Thresholds — what score counts as medium/high/block.
//   2. Allowlist — CIDRs that bypass scoring entirely.
//   3. Feedback inbox — host verdicts on past flagged accesses (the
//      seed corpus for the future ML model).
//
// Reads are viewer+; writes (PUT/POST/DELETE) are editor+. The server
// enforces the role gate; this component just hides write affordances
// from viewers so the buttons don't promise something a click would
// reject.

interface Thresholds {
  medium: number
  high: number
  block: number
  defaults: { medium: number; high: number; block: number }
}

interface AllowlistEntry {
  event_id: string
  cidr: string
  label?: string
  created_at: string
}

interface FeedbackEntry {
  access_log_id: string
  verdict: 'legitimate' | 'suspicious'
  note?: string
  created_at: string
}

const props = defineProps<{
  eventId: string
  yourRole?: 'owner' | 'editor' | 'viewer' | null
}>()

const canEdit = computed(() => props.yourRole === 'owner' || props.yourRole === 'editor')

// --- state ---
const loading = ref(true)
const error = ref<string | null>(null)
const saving = ref(false)

const thresholds = ref<Thresholds>({ medium: 30, high: 60, block: 85, defaults: { medium: 30, high: 60, block: 85 } })
const allowlist = ref<AllowlistEntry[]>([])
const feedback = ref<FeedbackEntry[]>([])

// Inline-add form for new CIDR rows.
const newCIDR = ref('')
const newLabel = ref('')
const addingCIDR = ref(false)

// Toast — same shape as the BrandingCard toast.
type Toast = { kind: 'success' | 'error'; text: string }
const toast = ref<Toast | null>(null)
let toastTimer: ReturnType<typeof setTimeout> | null = null
function showToast(t: Toast, ms = 4000) {
  toast.value = t
  if (toastTimer) clearTimeout(toastTimer)
  toastTimer = setTimeout(() => { toast.value = null }, ms)
}

async function refresh() {
  loading.value = true
  error.value = null
  try {
    const [th, al, fb] = await Promise.all([
      useApi<Thresholds>(`/events/${props.eventId}/security/thresholds`),
      useApi<{ entries: AllowlistEntry[] }>(`/events/${props.eventId}/security/allowlist`),
      useApi<{ feedback: FeedbackEntry[] }>(`/events/${props.eventId}/security/feedback`),
    ])
    thresholds.value = th
    allowlist.value = al.entries || []
    feedback.value = fb.feedback || []
  } catch (e: any) {
    error.value = useErrMessage(e, 'Could not load security settings')
  } finally {
    loading.value = false
  }
}
onMounted(refresh)

// --- thresholds ---

// Keep the three sliders mutually ordered: dragging medium past high
// pushes high; same for high past block. Without this the host can
// momentarily produce an invalid ordering and submit it — server
// would 400 but the form feels broken.
function clampOrder(which: 'medium' | 'high' | 'block') {
  if (which === 'medium' && thresholds.value.medium > thresholds.value.high) {
    thresholds.value.high = thresholds.value.medium
  }
  if (which === 'high') {
    if (thresholds.value.high < thresholds.value.medium) thresholds.value.medium = thresholds.value.high
    if (thresholds.value.high > thresholds.value.block) thresholds.value.block = thresholds.value.high
  }
  if (which === 'block' && thresholds.value.block < thresholds.value.high) {
    thresholds.value.high = thresholds.value.block
  }
}

async function saveThresholds() {
  saving.value = true
  try {
    await useApi(`/events/${props.eventId}/security/thresholds`, {
      method: 'PUT',
      body: {
        medium: thresholds.value.medium,
        high: thresholds.value.high,
        block: thresholds.value.block,
      },
    })
    showToast({ kind: 'success', text: 'Thresholds saved.' })
  } catch (e: any) {
    showToast({ kind: 'error', text: useErrMessage(e, 'Could not save thresholds') })
  } finally {
    saving.value = false
  }
}

function resetThresholds() {
  thresholds.value.medium = thresholds.value.defaults.medium
  thresholds.value.high = thresholds.value.defaults.high
  thresholds.value.block = thresholds.value.defaults.block
}

// --- allowlist ---

async function addCIDR() {
  if (!newCIDR.value.trim()) return
  addingCIDR.value = true
  try {
    await useApi(`/events/${props.eventId}/security/allowlist`, {
      method: 'POST',
      body: { cidr: newCIDR.value.trim(), label: newLabel.value.trim() },
    })
    newCIDR.value = ''
    newLabel.value = ''
    showToast({ kind: 'success', text: 'Allowlist entry added.' })
    await refresh()
  } catch (e: any) {
    showToast({ kind: 'error', text: useErrMessage(e, 'Could not add allowlist entry') })
  } finally {
    addingCIDR.value = false
  }
}

async function removeCIDR(entry: AllowlistEntry) {
  if (!confirm(`Remove allowlist entry ${entry.cidr}?`)) return
  try {
    await useApi(
      `/events/${props.eventId}/security/allowlist?cidr=${encodeURIComponent(entry.cidr)}`,
      { method: 'DELETE' },
    )
    showToast({ kind: 'success', text: 'Allowlist entry removed.' })
    await refresh()
  } catch (e: any) {
    showToast({ kind: 'error', text: useErrMessage(e, 'Could not remove allowlist entry') })
  }
}

function fmtDate(iso?: string | null) {
  if (!iso) return ''
  try { return new Date(iso).toLocaleDateString() } catch { return iso }
}
</script>

<template>
  <section class="card">
    <header class="mb-3 flex items-center justify-between">
      <div>
        <h2 class="text-lg font-semibold">Security</h2>
        <p class="text-xs text-zinc-500">
          Tune the fraud detector for this event.
        </p>
      </div>
    </header>

    <p v-if="error" class="mb-3 text-sm text-red-400">{{ error }}</p>
    <p v-if="loading" class="text-sm text-zinc-500">Loading security settings…</p>

    <div v-else class="space-y-8">
      <!-- Thresholds -->
      <div>
        <div class="mb-3 flex items-center justify-between">
          <div>
            <h3 class="text-sm font-semibold text-zinc-100">Risk thresholds</h3>
            <p class="text-xs text-zinc-500">
              Scores at or above each cut-off land in that band. Defaults work for most events.
            </p>
          </div>
          <div class="flex items-center gap-2">
            <button
              v-if="canEdit"
              type="button"
              class="text-xs text-zinc-400 hover:text-zinc-200"
              @click="resetThresholds"
            >Reset to defaults</button>
            <button
              v-if="canEdit"
              type="button"
              class="btn-primary text-sm"
              :disabled="saving"
              @click="saveThresholds"
            >{{ saving ? 'Saving…' : 'Save thresholds' }}</button>
          </div>
        </div>

        <!-- Visual band ribbon: each segment width matches its threshold
             range. Gives the host an at-a-glance read of where their
             cut-offs sit, instead of three abstract numbers. -->
        <div class="mb-4 h-2 w-full overflow-hidden rounded-full bg-zinc-900">
          <div class="flex h-full w-full">
            <div :style="{ width: thresholds.medium + '%' }" class="bg-brand-700"></div>
            <div :style="{ width: (thresholds.high - thresholds.medium) + '%' }" class="bg-amber-700"></div>
            <div :style="{ width: (thresholds.block - thresholds.high) + '%' }" class="bg-orange-700"></div>
            <div :style="{ width: (100 - thresholds.block) + '%' }" class="bg-red-700"></div>
          </div>
        </div>

        <div class="grid grid-cols-1 gap-4 md:grid-cols-3">
          <label class="block text-sm">
            <span class="mb-1 flex items-center justify-between text-xs text-zinc-400">
              <span><span class="inline-block h-2 w-2 rounded-full bg-brand-500 mr-1.5"></span>Medium</span>
              <span class="font-mono tabular-nums text-zinc-300">{{ thresholds.medium }}</span>
            </span>
            <input
              v-model.number="thresholds.medium"
              type="range"
              min="0"
              max="100"
              :disabled="!canEdit"
              class="w-full accent-brand-500"
              @input="clampOrder('medium')"
            />
          </label>
          <label class="block text-sm">
            <span class="mb-1 flex items-center justify-between text-xs text-zinc-400">
              <span><span class="inline-block h-2 w-2 rounded-full bg-amber-500 mr-1.5"></span>High</span>
              <span class="font-mono tabular-nums text-zinc-300">{{ thresholds.high }}</span>
            </span>
            <input
              v-model.number="thresholds.high"
              type="range"
              min="0"
              max="100"
              :disabled="!canEdit"
              class="w-full accent-amber-500"
              @input="clampOrder('high')"
            />
          </label>
          <label class="block text-sm">
            <span class="mb-1 flex items-center justify-between text-xs text-zinc-400">
              <span><span class="inline-block h-2 w-2 rounded-full bg-red-500 mr-1.5"></span>Block</span>
              <span class="font-mono tabular-nums text-zinc-300">{{ thresholds.block }}</span>
            </span>
            <input
              v-model.number="thresholds.block"
              type="range"
              min="0"
              max="100"
              :disabled="!canEdit"
              class="w-full accent-red-500"
              @input="clampOrder('block')"
            />
          </label>
        </div>
      </div>

      <!-- Allowlist -->
      <div>
        <h3 class="mb-1 text-sm font-semibold text-zinc-100">IP allowlist</h3>
        <p class="mb-3 text-xs text-zinc-500">
          Networks that bypass scoring. Use for office Wi-Fi, the venue's
          guest network, or a family router. Accepts a single IP or a CIDR
          range. Score is forced to 0 for matching requests.
        </p>

        <div v-if="canEdit" class="mb-3 flex items-end gap-2">
          <div class="flex-1">
            <label class="label">CIDR</label>
            <input
              v-model="newCIDR"
              type="text"
              placeholder="203.0.113.0/24 or 10.0.0.42"
              class="input font-mono text-sm"
            />
          </div>
          <div class="flex-1">
            <label class="label">Label (optional)</label>
            <input
              v-model="newLabel"
              type="text"
              placeholder="Office Wi-Fi"
              class="input text-sm"
            />
          </div>
          <button
            type="button"
            class="btn-primary text-sm"
            :disabled="addingCIDR || !newCIDR.trim()"
            @click="addCIDR"
          >{{ addingCIDR ? 'Adding…' : 'Add' }}</button>
        </div>

        <ul v-if="allowlist.length" class="space-y-1.5">
          <li
            v-for="entry in allowlist"
            :key="entry.cidr"
            class="flex items-center justify-between rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2"
          >
            <div class="min-w-0 flex-1">
              <p class="font-mono text-sm text-zinc-100">{{ entry.cidr }}</p>
              <p class="text-xs text-zinc-500">
                {{ entry.label || 'no label' }} · added {{ fmtDate(entry.created_at) }}
              </p>
            </div>
            <button
              v-if="canEdit"
              type="button"
              class="text-xs text-zinc-400 hover:text-red-300"
              @click="removeCIDR(entry)"
            >Remove</button>
          </li>
        </ul>
        <p v-else class="text-sm text-zinc-500">No allowlist entries yet.</p>
      </div>

      <!-- Feedback inbox — read-only summary; the actual "mark legitimate / suspicious"
           lives on the live monitor / activity feed where the host sees the
           access in context. This is the audit trail. -->
      <div>
        <h3 class="mb-1 text-sm font-semibold text-zinc-100">Verdict history</h3>
        <p class="mb-3 text-xs text-zinc-500">
          Your previous "legitimate" / "suspicious" marks on flagged accesses. Seeds the model and silences known false positives.
        </p>
        <ul v-if="feedback.length" class="space-y-1.5">
          <li
            v-for="f in feedback.slice(0, 10)"
            :key="f.access_log_id"
            class="flex items-center justify-between rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-sm"
          >
            <div class="min-w-0 flex-1">
              <p class="text-zinc-200">
                Marked <strong :class="f.verdict === 'legitimate' ? 'text-brand-300' : 'text-red-300'">{{ f.verdict }}</strong>
                <span class="ml-1 text-xs text-zinc-500">· {{ fmtDate(f.created_at) }}</span>
              </p>
              <p v-if="f.note" class="truncate text-xs text-zinc-500">{{ f.note }}</p>
            </div>
          </li>
          <li v-if="feedback.length > 10" class="text-xs text-zinc-500">
            and {{ feedback.length - 10 }} more.
          </li>
        </ul>
        <p v-else class="text-sm text-zinc-500">No verdicts yet.</p>
      </div>
    </div>

    <Teleport to="body">
      <Transition
        enter-active-class="transition duration-200 ease-out"
        enter-from-class="translate-y-2 opacity-0"
        enter-to-class="translate-y-0 opacity-100"
        leave-active-class="transition duration-200 ease-in"
        leave-from-class="translate-y-0 opacity-100"
        leave-to-class="translate-y-2 opacity-0"
      >
        <button
          v-if="toast"
          type="button"
          class="fixed bottom-6 right-6 z-50 flex max-w-sm items-center gap-2 rounded-lg border px-4 py-3 text-left text-sm shadow-lg backdrop-blur"
          :class="toast.kind === 'success'
            ? 'border-brand-700/60 bg-brand-950/90 text-brand-100'
            : 'border-red-800/60 bg-red-950/90 text-red-100'"
          role="status"
          @click="toast = null"
        >{{ toast.text }}</button>
      </Transition>
    </Teleport>
  </section>
</template>
