import { useCallback, useEffect, useMemo, useState } from "react"
import {
  CheckCircle2,
  Megaphone,
  Plus,
  RefreshCw,
  Trash2,
} from "lucide-react"

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

import { AppShell } from "~/components/layout/app-shell"
import { Badge } from "~/components/ui/badge"
import { Button } from "~/components/ui/button"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "~/components/ui/card"
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  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 { Switch } from "~/components/ui/switch"
import { Textarea } from "~/components/ui/textarea"
import {
  createAnnouncement,
  deleteAnnouncement,
  listAnnouncements,
  updateAnnouncement,
  type Announcement,
  type AnnouncementInput,
  type AnnouncementType,
} from "~/lib/arcadia/announcements"
import { listTenants, type Tenant } from "~/lib/arcadia/tenants"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterAdminContext } from "~/lib/admin-context"

export const meta = () => pageTitle("Announcements")

const TYPES: AnnouncementType[] = ["info", "warning", "maintenance", "incident", "feature"]

type Editor =
  | { kind: "create" }
  | { kind: "edit"; announcement: Announcement }
  | null

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

  const [items, setItems] = useState<Announcement[]>([])
  const [tenants, setTenants] = useState<Tenant[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [info, setInfo] = useState<string | null>(null)
  const [search, setSearch] = useState("")
  const [editor, setEditor] = useState<Editor>(null)
  const [pendingDelete, setPendingDelete] = useState<Announcement | null>(null)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const [a, t] = await Promise.all([
        listAnnouncements(arcadia),
        listTenants(arcadia).catch(() => [] as Tenant[]),
      ])
      setItems(a)
      setTenants(t)
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load announcements.")
    } finally {
      setLoading(false)
    }
  }, [arcadia])

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

  const columns = useMemo<Column<Announcement>[]>(
    () => [
      {
        id: "title",
        header: "Title",
        accessor: "title",
        sortable: true,
        cell: (a) => (
          <div className="flex flex-col">
            <span className="font-medium">{a.title}</span>
            {a.body ? (
              <span className="line-clamp-1 text-xs text-muted-foreground">{a.body}</span>
            ) : null}
          </div>
        ),
      },
      {
        id: "type",
        header: "Type",
        accessor: "announcement_type",
        sortable: true,
        cell: (a) => <BadgeCell label={a.announcement_type} tone={typeTone(a.announcement_type)} />,
      },
      {
        id: "scope",
        header: "Scope",
        cell: (a) =>
          a.tenant_id ? (
            <Badge variant="secondary">tenant</Badge>
          ) : (
            <Badge>platform</Badge>
          ),
      },
      {
        id: "active",
        header: "Active",
        accessor: "active",
        sortable: true,
        cell: (a) => (
          <BadgeCell label={a.active ? "live" : "off"} tone={a.active ? "success" : "default"} />
        ),
      },
      {
        id: "window",
        header: "Window",
        cell: (a) => (
          <span className="text-xs text-muted-foreground">
            {a.starts_at ? new Date(a.starts_at).toLocaleDateString() : "—"}
            {" → "}
            {a.ends_at ? new Date(a.ends_at).toLocaleDateString() : "∞"}
          </span>
        ),
      },
      {
        id: "updated",
        header: "Updated",
        accessor: "updated_at",
        sortable: true,
        cell: (a) => <DateCell value={a.updated_at} format="short" />,
      },
      {
        id: "actions",
        header: "",
        align: "right",
        cell: (a) => {
          const items: ActionItem[] = [
            {
              id: "edit",
              label: "Edit",
              dataAction: `announcement-${a.id}-edit`,
              onSelect: () => setEditor({ kind: "edit", announcement: a }),
            },
            {
              id: "toggle",
              label: a.active ? "Deactivate" : "Activate",
              dataAction: `announcement-${a.id}-toggle`,
              onSelect: async () => {
                try {
                  await updateAnnouncement(arcadia, a.id, { active: !a.active })
                  setInfo(a.active ? "Announcement deactivated." : "Announcement activated.")
                  await refresh()
                } catch (err) {
                  setError(err instanceof ArcadiaError ? err.message : "Toggle failed.")
                }
              },
            },
            {
              id: "delete",
              label: "Delete",
              icon: <Trash2 className="size-4" />,
              destructive: true,
              dataAction: `announcement-${a.id}-delete`,
              onSelect: () => setPendingDelete(a),
            },
          ]
          return <ActionsCell items={items} triggerDataAction={`announcement-${a.id}-actions`} />
        },
      },
    ],
    [arcadia, refresh],
  )

  const summary = useMemo(
    () => ({
      total: items.length,
      active: items.filter((a) => a.active).length,
      byType: countBy(items, (a) => a.announcement_type),
    }),
    [items],
  )
  useRegisterAdminContext("announcements", summary)

  const table = useTable<Announcement>({
    data: items,
    columns,
    getRowId: (a) => a.id,
    initialPageSize: 25,
    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">Announcements</h1>
            <p className="text-sm text-muted-foreground">
              Platform-wide and per-tenant banners. Apps consuming arcadia surface these to users.
            </p>
          </div>
          <div className="flex items-center gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={refresh}
              disabled={loading}
              data-action="announcements-refresh"
            >
              <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
              Refresh
            </Button>
            <Button
              size="sm"
              onClick={() => setEditor({ kind: "create" })}
              data-action="announcements-create"
            >
              <Plus className="size-4" />
              New announcement
            </Button>
          </div>
        </header>

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

        <Card>
          <CardHeader className="flex flex-row items-center gap-3">
            <SearchInput
              value={search}
              onValueChange={setSearch}
              placeholder="Search by title, body, or type"
              data-action="announcements-search"
              className="max-w-sm flex-1"
            />
            <div className="ml-auto text-xs text-muted-foreground">
              {table.total} of {items.length}
            </div>
          </CardHeader>

          <CardContent className="relative p-0">
            <LoadingOverlay
              active={loading && items.length === 0}
              label="Loading announcements…"
            />
            {table.total === 0 && !loading ? (
              <EmptyState
                icon={<Megaphone className="size-6" />}
                title={search ? "No announcements match." : "No announcements yet."}
                description={
                  search ? "Try a different search." : "Post the first one — platform-wide or scoped to a tenant."
                }
                className="py-12"
              />
            ) : (
              <>
                <DataTable
                  columns={columns}
                  rows={table.pageRows}
                  getRowId={(a) => a.id}
                  sort={table.sort}
                  onSortToggle={table.toggleSort}
                  loading={loading && items.length > 0}
                  stickyHeader
                />
                <Pagination
                  page={table.page}
                  pageSize={table.pageSize}
                  total={table.total}
                  onPageChange={table.setPage}
                  onPageSizeChange={table.setPageSize}
                />
              </>
            )}
          </CardContent>
        </Card>
      </div>

      <ConfirmDialog
        open={pendingDelete !== null}
        onOpenChange={(o) => !o && setPendingDelete(null)}
        title="Delete announcement?"
        description={pendingDelete ? `${pendingDelete.title} will be removed for all users.` : ""}
        confirmLabel="Delete"
        variant="danger"
        onConfirm={async () => {
          if (!pendingDelete) return
          try {
            await deleteAnnouncement(arcadia, pendingDelete.id)
            setPendingDelete(null)
            setInfo("Announcement deleted.")
            await refresh()
          } catch (err) {
            setError(err instanceof ArcadiaError ? err.message : "Delete failed.")
            setPendingDelete(null)
          }
        }}
      />

      <AnnouncementEditorDialog
        state={editor}
        tenants={tenants}
        onClose={() => setEditor(null)}
        onSaved={async (msg) => {
          setEditor(null)
          if (msg) setInfo(msg)
          await refresh()
        }}
        onError={setError}
      />
    </AppShell>
  )
}

function typeTone(t: AnnouncementType): BadgeTone {
  if (t === "incident") return "danger"
  if (t === "warning" || t === "maintenance") return "warning"
  if (t === "feature") return "success"
  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
  }, {})
}

function AnnouncementEditorDialog({
  state,
  tenants,
  onClose,
  onSaved,
  onError,
}: {
  state: Editor
  tenants: Tenant[]
  onClose: () => void
  onSaved: (msg?: string) => Promise<void>
  onError: (msg: string | null) => void
}) {
  const arcadia = useArcadiaClient()
  const open = state !== null
  const isEdit = state?.kind === "edit"
  const initial = isEdit ? state.announcement : null

  const [title, setTitle] = useState("")
  const [body, setBody] = useState("")
  const [type, setType] = useState<AnnouncementType>("info")
  const [audience, setAudience] = useState<"platform" | "tenant">("platform")
  const [tenantId, setTenantId] = useState<string>("")
  const [actionLabel, setActionLabel] = useState("")
  const [actionUrl, setActionUrl] = useState("")
  const [startsAt, setStartsAt] = useState("")
  const [endsAt, setEndsAt] = useState("")
  const [dismissible, setDismissible] = useState(true)
  const [active, setActive] = useState(true)
  const [saving, setSaving] = useState(false)

  useEffect(() => {
    if (!open) return
    if (initial) {
      setTitle(initial.title)
      setBody(initial.body ?? "")
      setType(initial.announcement_type)
      setAudience(initial.tenant_id ? "tenant" : "platform")
      setTenantId(initial.tenant_id ?? "")
      setActionLabel(initial.action_label ?? "")
      setActionUrl(initial.action_url ?? "")
      setStartsAt(initial.starts_at ? initial.starts_at.slice(0, 16) : "")
      setEndsAt(initial.ends_at ? initial.ends_at.slice(0, 16) : "")
      setDismissible(initial.dismissible)
      setActive(initial.active)
    } else {
      setTitle("")
      setBody("")
      setType("info")
      setAudience("platform")
      setTenantId("")
      setActionLabel("")
      setActionUrl("")
      setStartsAt("")
      setEndsAt("")
      setDismissible(true)
      setActive(true)
    }
  }, [open, initial])

  const submit = async () => {
    onError(null)
    setSaving(true)
    try {
      const input: AnnouncementInput = {
        title,
        body: body || undefined,
        announcement_type: type,
        audience,
        action_label: actionLabel || null,
        action_url: actionUrl || null,
        starts_at: startsAt ? new Date(startsAt).toISOString() : null,
        ends_at: endsAt ? new Date(endsAt).toISOString() : null,
        dismissible,
        active,
        tenant_id: audience === "tenant" ? tenantId || null : null,
      }
      if (isEdit && initial) {
        await updateAnnouncement(arcadia, initial.id, input)
        await onSaved("Announcement updated.")
      } else {
        await createAnnouncement(arcadia, input)
        await onSaved("Announcement posted.")
      }
    } catch (err) {
      onError(
        err instanceof ArcadiaError
          ? err.message
          : err instanceof Error
            ? err.message
            : "Save failed.",
      )
    } finally {
      setSaving(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-xl max-h-[90vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle>{isEdit ? "Edit announcement" : "New announcement"}</DialogTitle>
          <DialogDescription>
            Banners surface in apps that consume arcadia. Active + currently within the start/end
            window = visible.
          </DialogDescription>
        </DialogHeader>

        <div className="grid grid-cols-2 gap-3">
          <div className="col-span-2 flex flex-col gap-1.5">
            <Label htmlFor="ann-title">Title</Label>
            <Input
              id="ann-title"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              data-action="announcement-form-title"
            />
          </div>
          <div className="col-span-2 flex flex-col gap-1.5">
            <Label htmlFor="ann-body">Body</Label>
            <Textarea
              id="ann-body"
              value={body}
              onChange={(e) => setBody(e.target.value)}
              rows={4}
              data-action="announcement-form-body"
            />
          </div>

          <div className="flex flex-col gap-1.5">
            <Label>Type</Label>
            <Select value={type} onValueChange={setType}>
              <SelectTrigger data-action="announcement-form-type">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {TYPES.map((t) => (
                  <SelectItem key={t} value={t}>
                    {t}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>

          <div className="flex flex-col gap-1.5">
            <Label>Audience</Label>
            <Select value={audience} onValueChange={(v) => setAudience(v as "platform" | "tenant")}>
              <SelectTrigger data-action="announcement-form-audience">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="platform">Platform-wide</SelectItem>
                <SelectItem value="tenant">Single tenant</SelectItem>
              </SelectContent>
            </Select>
          </div>

          {audience === "tenant" ? (
            <div className="col-span-2 flex flex-col gap-1.5">
              <Label>Tenant</Label>
              <Select value={tenantId} onValueChange={setTenantId}>
                <SelectTrigger data-action="announcement-form-tenant">
                  <SelectValue placeholder="Pick a tenant" />
                </SelectTrigger>
                <SelectContent>
                  {tenants.map((t) => (
                    <SelectItem key={t.id} value={t.id}>
                      {t.name} ({t.slug})
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          ) : null}

          <div className="flex flex-col gap-1.5">
            <Label htmlFor="ann-starts">Starts at</Label>
            <Input
              id="ann-starts"
              type="datetime-local"
              value={startsAt}
              onChange={(e) => setStartsAt(e.target.value)}
              data-action="announcement-form-starts"
            />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="ann-ends">Ends at</Label>
            <Input
              id="ann-ends"
              type="datetime-local"
              value={endsAt}
              onChange={(e) => setEndsAt(e.target.value)}
              data-action="announcement-form-ends"
            />
          </div>

          <div className="flex flex-col gap-1.5">
            <Label htmlFor="ann-action-label">Action label (optional)</Label>
            <Input
              id="ann-action-label"
              value={actionLabel}
              onChange={(e) => setActionLabel(e.target.value)}
              placeholder="Read more"
              data-action="announcement-form-action-label"
            />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="ann-action-url">Action URL (optional)</Label>
            <Input
              id="ann-action-url"
              value={actionUrl}
              onChange={(e) => setActionUrl(e.target.value)}
              placeholder="/changelog/v2"
              data-action="announcement-form-action-url"
            />
          </div>

          <div className="flex items-center justify-between rounded-md border px-3 py-2">
            <Label className="text-sm">Dismissible</Label>
            <Switch
              checked={dismissible}
              onCheckedChange={setDismissible}
              data-action="announcement-form-dismissible"
            />
          </div>
          <div className="flex items-center justify-between rounded-md border px-3 py-2">
            <Label className="text-sm">Active</Label>
            <Switch
              checked={active}
              onCheckedChange={setActive}
              data-action="announcement-form-active"
            />
          </div>
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={onClose} disabled={saving}>
            Cancel
          </Button>
          <Button
            onClick={submit}
            disabled={saving || !title.trim() || (audience === "tenant" && !tenantId)}
            data-action="announcement-form-save"
          >
            {saving ? <RefreshCw className="size-4 animate-spin" /> : <CheckCircle2 className="size-4" />}
            {isEdit ? "Save" : "Post"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}
