import { useCallback, useEffect, useMemo, useState } from "react"
import {
  CheckCircle2,
  Network,
  Pause,
  Play,
  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 {
  activateMembership,
  createMembership,
  deleteMembership,
  listMemberships,
  suspendMembership,
  updateMembership,
  type Membership,
  type MembershipStatus,
} from "~/lib/arcadia/memberships"
import { listUsers, type User } from "~/lib/arcadia/users"
import { listRoles, type Role } from "~/lib/arcadia/roles"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterContext } from "@crema/aifirst-ui/context"

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

type Editor =
  | { kind: "create" }
  | { kind: "edit"; membership: Membership }
  | null

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

  const [memberships, setMemberships] = useState<Membership[]>([])
  const [users, setUsers] = useState<User[]>([])
  const [roles, setRoles] = useState<Role[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [info, setInfo] = useState<string | null>(null)
  const [search, setSearch] = useState("")
  const [statusFilter, setStatusFilter] = useState<"all" | MembershipStatus>("all")
  const [editor, setEditor] = useState<Editor>(null)
  const [pendingDelete, setPendingDelete] = useState<Membership | null>(null)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const [m, u, r] = await Promise.all([
        listMemberships(arcadia),
        listUsers(arcadia).catch(() => [] as User[]),
        listRoles(arcadia).catch(() => [] as Role[]),
      ])
      setMemberships(m)
      setUsers(u)
      setRoles(r)
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load memberships.")
    } finally {
      setLoading(false)
    }
  }, [arcadia])

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

  const filtered = useMemo(
    () =>
      statusFilter === "all"
        ? memberships
        : memberships.filter((m) => m.status === statusFilter),
    [memberships, statusFilter],
  )

  const columns = useMemo<Column<Membership>[]>(
    () => [
      {
        id: "user",
        header: "User",
        accessor: (m) => m.user?.email ?? m.user_id,
        sortable: true,
        cell: (m) => (
          <div className="flex flex-col">
            <span className="text-sm font-medium">{m.user?.email ?? "—"}</span>
            <span className="text-xs text-muted-foreground">
              {m.user?.first_name || m.user?.last_name
                ? `${m.user?.first_name ?? ""} ${m.user?.last_name ?? ""}`.trim()
                : m.user_id.slice(0, 8) + "…"}
            </span>
          </div>
        ),
      },
      {
        id: "tenant",
        header: "Tenant",
        accessor: (m) => m.tenant?.name ?? "",
        sortable: true,
        cell: (m) =>
          m.tenant ? (
            <div className="flex flex-col">
              <span className="text-sm font-medium">{m.tenant.name}</span>
              <code className="rounded bg-muted px-1 font-mono text-[10px] text-muted-foreground">
                {m.tenant.slug}
              </code>
            </div>
          ) : (
            <span className="text-muted-foreground">—</span>
          ),
      },
      {
        id: "status",
        header: "Status",
        accessor: "status",
        sortable: true,
        cell: (m) => <BadgeCell label={m.status} tone={statusTone(m.status)} />,
      },
      {
        id: "primary",
        header: "Primary",
        accessor: "is_primary",
        sortable: true,
        cell: (m) =>
          m.is_primary ? (
            <CheckCircle2 className="size-4 text-emerald-500" />
          ) : (
            <span className="text-muted-foreground">—</span>
          ),
      },
      {
        id: "roles",
        header: "Roles",
        cell: (m) =>
          m.roles.length === 0 ? (
            <span className="text-muted-foreground">—</span>
          ) : (
            <div className="flex flex-wrap gap-1">
              {m.roles.map((r) => (
                <Badge key={r.id} variant="secondary" className="font-mono text-xs">
                  {r.slug}
                </Badge>
              ))}
            </div>
          ),
      },
      {
        id: "joined",
        header: "Joined",
        accessor: "joined_at",
        sortable: true,
        cell: (m) =>
          m.joined_at ? (
            <DateCell value={m.joined_at} format="short" />
          ) : (
            <span className="text-muted-foreground">—</span>
          ),
      },
      {
        id: "actions",
        header: "",
        align: "right",
        cell: (m) => {
          const items: ActionItem[] = [
            {
              id: "edit",
              label: "Edit",
              dataAction: `membership-${m.id}-edit`,
              onSelect: () => setEditor({ kind: "edit", membership: m }),
            },
            m.status === "active"
              ? {
                  id: "suspend",
                  label: "Suspend",
                  icon: <Pause className="size-4" />,
                  dataAction: `membership-${m.id}-suspend`,
                  onSelect: async () => {
                    try {
                      await suspendMembership(arcadia, m.id)
                      setInfo("Membership suspended.")
                      await refresh()
                    } catch (err) {
                      setError(err instanceof ArcadiaError ? err.message : "Suspend failed.")
                    }
                  },
                }
              : {
                  id: "activate",
                  label: "Activate",
                  icon: <Play className="size-4" />,
                  dataAction: `membership-${m.id}-activate`,
                  onSelect: async () => {
                    try {
                      await activateMembership(arcadia, m.id)
                      setInfo("Membership activated.")
                      await refresh()
                    } catch (err) {
                      setError(err instanceof ArcadiaError ? err.message : "Activate failed.")
                    }
                  },
                },
            {
              id: "delete",
              label: "Remove",
              icon: <Trash2 className="size-4" />,
              destructive: true,
              dataAction: `membership-${m.id}-delete`,
              onSelect: () => setPendingDelete(m),
            },
          ]
          return (
            <ActionsCell items={items} triggerDataAction={`membership-${m.id}-actions`} />
          )
        },
      },
    ],
    [arcadia, refresh],
  )

  const summary = useMemo(
    () => ({
      total: memberships.length,
      byStatus: countBy(memberships, (m) => m.status),
      uniqueTenants: new Set(memberships.map((m) => m.tenant_id)).size,
      uniqueUsers: new Set(memberships.map((m) => m.user_id)).size,
    }),
    [memberships],
  )
  useRegisterContext("memberships", summary)

  const table = useTable<Membership>({
    data: filtered,
    columns,
    getRowId: (m) => m.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">Memberships</h1>
            <p className="text-sm text-muted-foreground">
              Who belongs to which tenant. A user can have memberships in multiple tenants;
              one is marked primary.
            </p>
          </div>
          <div className="flex items-center gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={refresh}
              disabled={loading}
              data-action="memberships-refresh"
            >
              <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
              Refresh
            </Button>
            <Button
              size="sm"
              onClick={() => setEditor({ kind: "create" })}
              data-action="memberships-create"
            >
              <Plus className="size-4" />
              Add member
            </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 user, tenant, or role"
              data-action="memberships-search"
              className="max-w-sm flex-1"
            />
            <Select
              value={statusFilter}
              onValueChange={(v) => setStatusFilter(v as typeof statusFilter)}
            >
              <SelectTrigger className="w-40" data-action="memberships-status-filter">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="all">All statuses</SelectItem>
                <SelectItem value="active">Active</SelectItem>
                <SelectItem value="suspended">Suspended</SelectItem>
                <SelectItem value="deactivated">Deactivated</SelectItem>
              </SelectContent>
            </Select>
            <div className="ml-auto text-xs text-muted-foreground">
              {table.total} of {memberships.length}
            </div>
          </CardHeader>

          <CardContent className="relative p-0">
            <LoadingOverlay
              active={loading && memberships.length === 0}
              label="Loading memberships…"
            />
            {table.total === 0 && !loading ? (
              <EmptyState
                icon={<Network className="size-6" />}
                title={
                  search || statusFilter !== "all"
                    ? "No memberships match those filters."
                    : "No memberships yet."
                }
                description={
                  search || statusFilter !== "all"
                    ? "Loosen the filter set."
                    : "Add a user to a tenant to create the first membership."
                }
                className="py-12"
              />
            ) : (
              <>
                <DataTable
                  columns={columns}
                  rows={table.pageRows}
                  getRowId={(m) => m.id}
                  sort={table.sort}
                  onSortToggle={table.toggleSort}
                  loading={loading && memberships.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="Remove membership?"
        description={
          pendingDelete
            ? `${pendingDelete.user?.email ?? pendingDelete.user_id} will lose access to ${pendingDelete.tenant?.name ?? "this tenant"}.`
            : ""
        }
        confirmLabel="Remove"
        variant="danger"
        onConfirm={async () => {
          if (!pendingDelete) return
          try {
            await deleteMembership(arcadia, pendingDelete.id)
            setPendingDelete(null)
            setInfo("Membership removed.")
            await refresh()
          } catch (err) {
            setError(err instanceof ArcadiaError ? err.message : "Remove failed.")
            setPendingDelete(null)
          }
        }}
      />

      <MembershipEditorDialog
        state={editor}
        users={users}
        roles={roles}
        existingUserIds={new Set(memberships.map((m) => m.user_id))}
        onClose={() => setEditor(null)}
        onSaved={async (msg) => {
          setEditor(null)
          if (msg) setInfo(msg)
          await refresh()
        }}
        onError={setError}
      />
    </AppShell>
  )
}

function statusTone(s: MembershipStatus): BadgeTone {
  if (s === "active") return "success"
  if (s === "suspended") return "warning"
  return "default"
}

function MembershipEditorDialog({
  state,
  users,
  roles,
  existingUserIds,
  onClose,
  onSaved,
  onError,
}: {
  state: Editor
  users: User[]
  roles: Role[]
  existingUserIds: Set<string>
  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.membership : null

  const [userId, setUserId] = useState("")
  const [status, setStatus] = useState<MembershipStatus>("active")
  const [selectedRoles, setSelectedRoles] = useState<Set<string>>(new Set())
  const [saving, setSaving] = useState(false)

  useEffect(() => {
    if (!open) return
    if (initial) {
      setUserId(initial.user_id)
      setStatus(initial.status)
      setSelectedRoles(new Set(initial.roles.map((r) => r.id)))
    } else {
      setUserId("")
      setStatus("active")
      setSelectedRoles(new Set())
    }
  }, [open, initial])

  const eligibleUsers = useMemo(
    () => (isEdit ? users : users.filter((u) => !existingUserIds.has(u.id))),
    [users, existingUserIds, isEdit],
  )

  const submit = async () => {
    onError(null)
    setSaving(true)
    try {
      const input = {
        user_id: userId,
        status,
        role_ids: Array.from(selectedRoles),
      }
      if (isEdit && initial) {
        await updateMembership(arcadia, initial.id, input)
        await onSaved("Membership updated.")
      } else {
        await createMembership(arcadia, input)
        await onSaved("Member added.")
      }
    } 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-md">
        <DialogHeader>
          <DialogTitle>{isEdit ? "Edit membership" : "Add member"}</DialogTitle>
          <DialogDescription>
            {isEdit
              ? "Update status and role assignments."
              : "Pick a user and assign roles within the current tenant."}
          </DialogDescription>
        </DialogHeader>

        <div className="flex flex-col gap-3">
          <div className="flex flex-col gap-1.5">
            <Label>User</Label>
            <Select value={userId} onValueChange={setUserId} disabled={isEdit}>
              <SelectTrigger data-action="membership-form-user">
                <SelectValue placeholder="Pick a user" />
              </SelectTrigger>
              <SelectContent>
                {eligibleUsers.length === 0 ? (
                  <SelectItem value="__none" disabled>
                    No eligible users
                  </SelectItem>
                ) : (
                  eligibleUsers.map((u) => (
                    <SelectItem key={u.id} value={u.id}>
                      {u.email}
                    </SelectItem>
                  ))
                )}
              </SelectContent>
            </Select>
          </div>

          <div className="flex flex-col gap-1.5">
            <Label>Status</Label>
            <Select
              value={status}
              onValueChange={(v) => setStatus(v as MembershipStatus)}
            >
              <SelectTrigger data-action="membership-form-status">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="active">Active</SelectItem>
                <SelectItem value="suspended">Suspended</SelectItem>
                <SelectItem value="deactivated">Deactivated</SelectItem>
              </SelectContent>
            </Select>
          </div>

          <div className="flex flex-col gap-1.5">
            <Label>Roles</Label>
            {roles.length === 0 ? (
              <p className="text-xs text-muted-foreground">
                No roles defined. Create some on the Users tab.
              </p>
            ) : (
              <div className="flex flex-wrap gap-1.5 rounded-md border p-2">
                {roles.map((r) => {
                  const active = selectedRoles.has(r.id)
                  return (
                    <button
                      key={r.id}
                      type="button"
                      onClick={() => {
                        setSelectedRoles((prev) => {
                          const next = new Set(prev)
                          if (next.has(r.id)) next.delete(r.id)
                          else next.add(r.id)
                          return next
                        })
                      }}
                      data-action={`membership-form-role-${r.slug}`}
                      className={[
                        "rounded-full border px-2.5 py-1 text-xs font-medium transition-colors",
                        active
                          ? "border-primary bg-primary/10 text-primary"
                          : "border-border text-muted-foreground hover:bg-accent",
                      ].join(" ")}
                    >
                      {r.name}
                    </button>
                  )
                })}
              </div>
            )}
          </div>
        </div>

        <DialogFooter>
          <Button variant="outline" onClick={onClose} disabled={saving}>
            Cancel
          </Button>
          <Button
            onClick={submit}
            disabled={saving || !userId}
            data-action="membership-form-save"
          >
            {saving ? <RefreshCw className="size-4 animate-spin" /> : <CheckCircle2 className="size-4" />}
            {isEdit ? "Save" : "Add"}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

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
  }, {})
}

// File-local alias just to keep the Editor type narrowable inside the dialog.
type Editor =
  | { kind: "create" }
  | { kind: "edit"; membership: Membership }
  | null
