import { useCallback, useEffect, useMemo, useState } from "react"
import { Activity, Eye, RefreshCw } from "lucide-react"

import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-core-client"
import {
  ActionsCell,
  BadgeCell,
  DataTable,
  DateCell,
  Pagination,
  useTable,
  type BadgeTone,
  type Column,
} from "@crema/table-ui"
import { SearchInput } from "@crema/search-ui"
import { AlertBanner, EmptyState, LoadingOverlay } from "@crema/feedback-ui"

import { AppShell } from "~/components/layout/app-shell"
import { Button } from "~/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "~/components/ui/card"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "~/components/ui/dialog"
import { Input } from "~/components/ui/input"
import { Label } from "~/components/ui/label"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "~/components/ui/select"
import {
  listAuditLogs,
  type AuditLog,
  type AuditSeverity,
} from "~/lib/arcadia/audit-logs"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterContext } from "@crema/aifirst-ui/context"

export const meta = () => pageTitle("Audit log")

export default function ActivityRoute() {
  const session = useSession()
  const arcadia = useArcadiaClient()

  const [logs, setLogs] = useState<AuditLog[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [search, setSearch] = useState("")
  const [severityFilter, setSeverityFilter] = useState<"all" | AuditSeverity>("all")
  const [resourceFilter, setResourceFilter] = useState("")
  const [from, setFrom] = useState("")
  const [to, setTo] = useState("")
  const [detail, setDetail] = useState<AuditLog | null>(null)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const list = await listAuditLogs(arcadia, {
        severity: severityFilter === "all" ? undefined : severityFilter,
        resource_type: resourceFilter || undefined,
        from: from ? new Date(from).toISOString() : undefined,
        to: to ? new Date(to).toISOString() : undefined,
        limit: 200,
      })
      setLogs(list)
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load audit logs.")
    } finally {
      setLoading(false)
    }
  }, [arcadia, severityFilter, resourceFilter, from, to])

  useEffect(() => {
    if (session) refresh()
  }, [session, refresh])

  const columns = useMemo<Column<AuditLog>[]>(
    () => [
      {
        id: "time",
        header: "Time",
        accessor: "inserted_at",
        sortable: true,
        cell: (l) => <DateCell value={l.inserted_at} format="datetime" />,
      },
      {
        id: "user",
        header: "User",
        accessor: (l) => l.user?.email ?? "",
        sortable: true,
        cell: (l) => (
          <span className="text-sm">
            {l.user?.email ?? <span className="text-muted-foreground">system</span>}
          </span>
        ),
      },
      {
        id: "action",
        header: "Action",
        accessor: "action",
        sortable: true,
        cell: (l) => (
          <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">{l.action}</code>
        ),
      },
      {
        id: "resource",
        header: "Resource",
        accessor: "resource_type",
        sortable: true,
        cell: (l) => (
          <span className="text-sm">
            <span className="font-medium">{l.resource_type}</span>
            {l.resource_id ? (
              <span className="ml-1 font-mono text-xs text-muted-foreground">
                {l.resource_id.slice(0, 8)}…
              </span>
            ) : null}
          </span>
        ),
      },
      {
        id: "severity",
        header: "Severity",
        accessor: "severity",
        sortable: true,
        cell: (l) => <BadgeCell label={l.severity} tone={severityTone(l.severity)} />,
      },
      {
        id: "ip",
        header: "IP",
        accessor: "ip_address",
        cell: (l) => (
          <span className="font-mono text-xs text-muted-foreground">
            {l.ip_address ?? "—"}
          </span>
        ),
      },
      {
        id: "actions",
        header: "",
        align: "right",
        cell: (l) => (
          <ActionsCell
            items={[
              {
                id: "view",
                label: "View details",
                icon: <Eye className="size-4" />,
                dataAction: `audit-${l.id}-view`,
                onSelect: () => setDetail(l),
              },
            ]}
            triggerDataAction={`audit-${l.id}-actions`}
          />
        ),
      },
    ],
    [],
  )

  const summary = useMemo(
    () => ({
      total: logs.length,
      bySeverity: countBy(logs, (l) => l.severity || "info"),
      byResource: countBy(logs, (l) => l.resource_type),
      latest: logs.slice(0, 5).map((l) => ({
        time: l.inserted_at,
        user: l.user?.email ?? "system",
        action: l.action,
        resource: `${l.resource_type}${l.resource_id ? `/${l.resource_id}` : ""}`,
      })),
    }),
    [logs],
  )
  useRegisterContext("audit_log", summary)

  const table = useTable<AuditLog>({
    data: logs,
    columns,
    getRowId: (l) => l.id,
    initialPageSize: 50,
    initialSearch: search,
  })
  useEffect(() => {
    table.setSearch(search)
  }, [search, table])

  return (
    <AppShell>
      <div className="flex flex-col gap-4">
        <header className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
          <div>
            <h1 className="text-2xl font-semibold tracking-tight">Audit log</h1>
            <p className="text-sm text-muted-foreground">
              Every authenticated action against the platform. Filter by date, severity, or
              resource type.
            </p>
          </div>
          <Button
            variant="outline"
            size="sm"
            onClick={refresh}
            disabled={loading}
            data-action="audit-refresh"
          >
            <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
            Refresh
          </Button>
        </header>

        {error ? (
          <AlertBanner variant="error" dismissible onDismiss={() => setError(null)}>
            {error}
          </AlertBanner>
        ) : null}

        <Card>
          <CardHeader className="flex flex-col gap-3 lg:flex-row lg:flex-wrap lg:items-end">
            <SearchInput
              value={search}
              onValueChange={setSearch}
              placeholder="Search by action, resource, or user"
              data-action="audit-search"
              className="max-w-sm flex-1"
            />
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="audit-severity" className="text-xs">
                Severity
              </Label>
              <Select
                value={severityFilter}
                onValueChange={(v) => setSeverityFilter(v as typeof severityFilter)}
              >
                <SelectTrigger id="audit-severity" className="w-36" data-action="audit-severity-filter">
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="all">All</SelectItem>
                  <SelectItem value="info">Info</SelectItem>
                  <SelectItem value="warning">Warning</SelectItem>
                  <SelectItem value="error">Error</SelectItem>
                  <SelectItem value="critical">Critical</SelectItem>
                </SelectContent>
              </Select>
            </div>
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="audit-resource" className="text-xs">
                Resource type
              </Label>
              <Input
                id="audit-resource"
                value={resourceFilter}
                onChange={(e) => setResourceFilter(e.target.value)}
                placeholder="e.g. user"
                className="w-40"
                data-action="audit-resource-filter"
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="audit-from" className="text-xs">
                From
              </Label>
              <Input
                id="audit-from"
                type="datetime-local"
                value={from}
                onChange={(e) => setFrom(e.target.value)}
                className="w-44"
                data-action="audit-from-filter"
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="audit-to" className="text-xs">
                To
              </Label>
              <Input
                id="audit-to"
                type="datetime-local"
                value={to}
                onChange={(e) => setTo(e.target.value)}
                className="w-44"
                data-action="audit-to-filter"
              />
            </div>
          </CardHeader>

          <CardContent className="relative p-0">
            <LoadingOverlay active={loading && logs.length === 0} label="Loading audit log…" />
            {table.total === 0 && !loading ? (
              <EmptyState
                icon={<Activity className="size-6" />}
                title="No events match those filters."
                description="Loosen the filter set or wait for new platform activity."
                className="py-12"
              />
            ) : (
              <>
                <DataTable
                  columns={columns}
                  rows={table.pageRows}
                  getRowId={(l) => l.id}
                  sort={table.sort}
                  onSortToggle={table.toggleSort}
                  loading={loading && logs.length > 0}
                  stickyHeader
                />
                <Pagination
                  page={table.page}
                  pageSize={table.pageSize}
                  total={table.total}
                  onPageChange={table.setPage}
                  onPageSizeChange={table.setPageSize}
                />
              </>
            )}
          </CardContent>
        </Card>
      </div>

      <Dialog open={detail !== null} onOpenChange={(o) => !o && setDetail(null)}>
        <DialogContent className="sm:max-w-2xl max-h-[85vh] overflow-y-auto">
          <DialogHeader>
            <DialogTitle>Audit event</DialogTitle>
            <DialogDescription>
              {detail
                ? `${detail.action} on ${detail.resource_type} at ${new Date(
                    detail.inserted_at,
                  ).toLocaleString()}`
                : ""}
            </DialogDescription>
          </DialogHeader>
          {detail ? <AuditDetailBody log={detail} /> : null}
        </DialogContent>
      </Dialog>
    </AppShell>
  )
}

function AuditDetailBody({ log }: { log: AuditLog }) {
  const rows: { k: string; v: string }[] = [
    { k: "ID", v: log.id },
    { k: "Tenant", v: log.tenant_id },
    { k: "User", v: log.user?.email ?? log.user_id ?? "—" },
    { k: "Action", v: log.action },
    { k: "Resource", v: `${log.resource_type}${log.resource_id ? `/${log.resource_id}` : ""}` },
    { k: "Severity", v: log.severity },
    { k: "IP", v: log.ip_address ?? "—" },
    { k: "User agent", v: log.user_agent ?? "—" },
    { k: "Time", v: new Date(log.inserted_at).toISOString() },
  ]
  return (
    <div className="flex flex-col gap-4">
      <dl className="grid grid-cols-[8rem_1fr] gap-y-1 text-sm">
        {rows.map((r) => (
          <div key={r.k} className="contents">
            <dt className="text-xs uppercase tracking-wide text-muted-foreground">{r.k}</dt>
            <dd className="break-all font-mono text-xs">{r.v}</dd>
          </div>
        ))}
      </dl>
      {log.changes ? (
        <div>
          <h3 className="mb-1.5 text-sm font-semibold">Changes</h3>
          <pre className="overflow-x-auto rounded-md border bg-muted/50 p-3 text-xs">
            {JSON.stringify(log.changes, null, 2)}
          </pre>
        </div>
      ) : null}
      {log.metadata && Object.keys(log.metadata).length > 0 ? (
        <div>
          <h3 className="mb-1.5 text-sm font-semibold">Metadata</h3>
          <pre className="overflow-x-auto rounded-md border bg-muted/50 p-3 text-xs">
            {JSON.stringify(log.metadata, null, 2)}
          </pre>
        </div>
      ) : null}
    </div>
  )
}

function severityTone(s: AuditSeverity): BadgeTone {
  if (s === "critical" || s === "error") return "danger"
  if (s === "warning") return "warning"
  return "default"
}

function countBy<T>(arr: T[], key: (x: T) => string): Record<string, number> {
  return arr.reduce<Record<string, number>>((acc, x) => {
    const k = key(x)
    acc[k] = (acc[k] ?? 0) + 1
    return acc
  }, {})
}
