<script lang="ts">
  import { onMount } from 'svelte'
  import {
    fetchApprovals,
    decideApproval,
    fetchRecentActivity,
    cancelExecution,
    type Approval,
    type ActivityItem
  } from '$lib/api'
  import { liveEvents, subscribeEvents } from '$lib/stores/events'
  import * as Tabs from '$lib/components/ui/tabs'
  import { Badge } from '$lib/components/ui/badge'
  import { toast } from 'svelte-sonner'
  import DataTable from '$lib/components/data-table/DataTable.svelte'
  import type { DataTableColumn } from '$lib/components/data-table/types'
  import ApprovalActions from '$lib/components/data-table/renderers/ApprovalActions.svelte'
  import ActivityCancel from '$lib/components/data-table/renderers/ActivityCancel.svelte'
  import ActivityAction from '$lib/components/data-table/renderers/ActivityAction.svelte'
  import DurationRenderer from '$lib/components/data-table/renderers/DurationRenderer.svelte'

  let approvals = $state<Approval[]>([])
  let activity = $state<ActivityItem[]>([])
  let deciding = $state<string | null>(null)

  async function loadApprovals() {
    approvals = await fetchApprovals()
  }
  async function loadActivity() {
    activity = await fetchRecentActivity()
  }

  onMount(() => {
    loadApprovals()
    loadActivity()
    const unsubscribe = subscribeEvents()
    const interval = setInterval(loadActivity, 5000)
    return () => {
      unsubscribe()
      clearInterval(interval)
    }
  })

  $effect(() => {
    const ev = $liveEvents[0]
    if (!ev) return
    if (ev.type.startsWith('approval.')) loadApprovals()
    // execution.output is a "more command output arrived" ping for one
    // execution, not a lifecycle change — it fires up to once a second per
    // running command and changes nothing this table shows. Refetching the
    // whole activity list on it would turn a chatty apt upgrade into a
    // refetch storm.
    if (ev.type.startsWith('execution.') && ev.type !== 'execution.output') loadActivity()
  })

  async function decide(id: string, decision: 'approve' | 'deny') {
    deciding = id
    const result = await decideApproval(id, decision)
    deciding = null
    if (result) {
      toast.success(`Approval ${decision === 'approve' ? 'approved' : 'denied'}`)
      loadApprovals()
    } else {
      toast.error('Decision failed')
    }
  }

  async function cancel(id: string) {
    const result = await cancelExecution(id)
    if (result) {
      toast.success('Execution cancelled')
      loadActivity()
    } else {
      toast.error('Cancel failed')
    }
  }

  const pendingApprovals = $derived(approvals.filter((a) => a.status === 'pending'))
  const decidedApprovals = $derived(approvals.filter((a) => a.status !== 'pending'))

  const pendingColumns = $derived.by(
    () =>
      [
        {
          key: 'subject',
          header: 'Subject',
          class: 'font-mono text-xs',
          width: '180px',
          accessor: (a: Approval) => a.subject ?? '—',
          truncate: true
        },
        { key: 'action', header: 'Action', truncate: true },
        {
          key: 'risk_class',
          header: 'Risk',
          render: 'status-badge',
          renderProps: { kind: 'risk' },
          width: '120px'
        },
        {
          key: 'status',
          header: 'Status',
          render: 'status-badge',
          renderProps: { kind: 'execution' },
          width: '100px'
        },
        { key: 'expires_at', header: 'Expires', render: 'date', width: '170px' },
        {
          key: '_actions',
          header: '',
          render: ApprovalActions,
          renderProps: {
            deciding,
            onApprove: (id: string) => decide(id, 'approve'),
            onDeny: (id: string) => decide(id, 'deny')
          },
          align: 'right',
          headerClass: 'text-right',
          width: '220px'
        }
      ] as DataTableColumn<Approval>[]
  )

  const decidedColumns: DataTableColumn<Approval>[] = [
    {
      key: 'subject',
      header: 'Subject',
      class: 'font-mono text-xs',
      width: '180px',
      accessor: (a) => a.subject ?? '—',
      truncate: true
    },
    { key: 'action', header: 'Action', truncate: true },
    {
      key: 'status',
      header: 'Status',
      render: 'status-badge',
      renderProps: { kind: 'execution' },
      width: '100px'
    },
    {
      key: 'decided_at',
      header: 'Decided',
      render: 'date',
      width: '170px',
      accessor: (a) => a.decided_at ?? '—'
    }
  ]

  const activityColumns: DataTableColumn<ActivityItem>[] = [
    {
      key: 'target',
      header: 'Target',
      class: 'font-mono text-xs',
      width: '180px',
      accessor: (a) => a.target ?? '—',
      truncate: true
    },
    { key: '_action', header: 'Action', render: ActivityAction, truncate: true },
    {
      key: 'risk_class',
      header: 'Risk',
      render: 'status-badge',
      renderProps: { kind: 'risk' },
      width: '120px'
    },
    {
      key: 'status',
      header: 'Status',
      render: 'status-badge',
      renderProps: { kind: 'execution' },
      width: '110px'
    },
    {
      key: 'duration_ms',
      header: 'Duration',
      render: DurationRenderer,
      width: '90px',
      align: 'right'
    },
    { key: 'created_at', header: 'When', render: 'relative-time', width: '100px' },
    {
      key: '_cancel',
      header: '',
      render: ActivityCancel,
      renderProps: { onCancel: cancel },
      align: 'right',
      headerClass: 'text-right',
      width: '100px'
    }
  ]
</script>

<div class="flex h-full flex-col gap-4 p-2">
  <h1 class="text-lg font-semibold">Operations ledger</h1>

  <Tabs.Root value="approvals" class="flex flex-1 flex-col overflow-hidden">
    <Tabs.List>
      <Tabs.Trigger value="approvals">
        Approvals {#if pendingApprovals.length}<Badge variant="destructive" class="ml-1"
            >{pendingApprovals.length}</Badge
          >{/if}
      </Tabs.Trigger>
      <Tabs.Trigger value="executions">Activity</Tabs.Trigger>
    </Tabs.List>

    <Tabs.Content value="approvals" class="flex-1 overflow-auto">
      <DataTable
        columns={pendingColumns}
        data={pendingApprovals}
        emptyMessage="No pending approvals."
      />

      {#if decidedApprovals.length}
        <p class="mt-4 text-xs text-muted-foreground">Recently decided</p>
        <div class="mt-1">
          <DataTable columns={decidedColumns} data={decidedApprovals.slice(0, 20)} />
        </div>
      {/if}
    </Tabs.Content>

    <Tabs.Content value="executions" class="flex-1 overflow-auto">
      <DataTable columns={activityColumns} data={activity} emptyMessage="No activity yet." />
    </Tabs.Content>
  </Tabs.Root>
</div>
