import { useCallback, useEffect, useMemo, useState, type FormEvent } from "react"
import { Pause, Play, Plus, RefreshCw } from "lucide-react"

import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-core-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 { PageHeader } from "~/components/layout/page-header"
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 {
  activateTenant,
  deactivateTenant,
  listTenants,
  provisionTenant,
  suspendTenant,
  type Tenant,
  type TenantStatus,
} from "~/lib/arcadia/tenants"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterContext } from "@crema/aifirst-ui/context"

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

type PendingAction = {
  kind: "suspend" | "deactivate"
  tenant: Tenant
} | null

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

  const [tenants, setTenants] = useState<Tenant[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [pending, setPending] = useState<PendingAction>(null)
  const [search, setSearch] = useState("")
  const [createOpen, setCreateOpen] = useState(false)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const list = await listTenants(arcadia)
      setTenants(list)
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load tenants.")
    } finally {
      setLoading(false)
    }
  }, [arcadia])

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

  const runAction = useCallback(
    async (action: PendingAction) => {
      if (!action) return
      try {
        if (action.kind === "suspend") await suspendTenant(arcadia, action.tenant.id)
        else await deactivateTenant(arcadia, action.tenant.id)
        setPending(null)
        await refresh()
      } catch (err) {
        setError(err instanceof ArcadiaError ? err.message : "Action failed.")
        setPending(null)
      }
    },
    [arcadia, refresh],
  )

  const columns = useMemo<Column<Tenant>[]>(
    () => [
      {
        id: "name",
        header: "Name",
        accessor: "name",
        sortable: true,
        cell: (t) => <span className="font-medium">{t.name}</span>,
      },
      {
        id: "slug",
        header: "Slug",
        accessor: "slug",
        sortable: true,
        cell: (t) => (
          <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">{t.slug}</code>
        ),
      },
      {
        id: "status",
        header: "Status",
        accessor: "status",
        sortable: true,
        cell: (t) => <BadgeCell label={t.status} tone={statusTone(t.status)} />,
      },
      {
        id: "plan",
        header: "Plan",
        accessor: (t) => t.plan?.name ?? "",
        sortable: true,
        cell: (t) => <span className="text-muted-foreground">{t.plan?.name ?? "—"}</span>,
      },
      {
        id: "created",
        header: "Created",
        accessor: "inserted_at",
        sortable: true,
        cell: (t) => <DateCell value={t.inserted_at} format="short" />,
      },
      {
        id: "actions",
        header: "",
        align: "right",
        cell: (t) => (
          <ActionsCell
            items={rowActions(t, arcadia, refresh, setPending, setError)}
            triggerDataAction={`tenant-${t.slug}-actions`}
          />
        ),
      },
    ],
    [arcadia, refresh],
  )

  const tenantSummary = useMemo(
    () => ({
      total: tenants.length,
      byStatus: tenants.reduce<Record<string, number>>((acc, t) => {
        acc[t.status] = (acc[t.status] ?? 0) + 1
        return acc
      }, {}),
      tenants: tenants.map((t) => ({
        id: t.id,
        slug: t.slug,
        name: t.name,
        status: t.status,
        plan: t.plan?.name ?? null,
        inserted_at: t.inserted_at,
      })),
    }),
    [tenants],
  )
  useRegisterContext("tenants", tenantSummary)

  const table = useTable<Tenant>({
    data: tenants,
    columns,
    getRowId: (t) => t.id,
    initialPageSize: 25,
    initialSearch: search,
  })
  // Keep useTable's search in lockstep with our SearchInput.
  useEffect(() => {
    table.setSearch(search)
  }, [search, table])

  return (
    <AppShell>
      <PageHeader
        title="Tenants"
        description="Multi-tenant workspaces on this arcadia deployment."
        actions={
          <>
            <Button
              variant="outline"
              size="sm"
              onClick={refresh}
              disabled={loading}
              data-action="tenants-refresh"
            >
              <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
              Refresh
            </Button>
            <Button
              size="sm"
              onClick={() => setCreateOpen(true)}
              data-action="tenants-create"
            >
              <Plus className="size-4" />
              New tenant
            </Button>
          </>
        }
      />

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

        <Card>
          <CardHeader className="flex flex-row items-center justify-between gap-4">
            <SearchInput
              value={search}
              onValueChange={setSearch}
              placeholder="Search by name, slug, or status"
              data-action="tenants-search"
              className="max-w-sm flex-1"
            />
            <div className="text-xs text-muted-foreground">
              {table.total} of {tenants.length}
            </div>
          </CardHeader>

          <CardContent className="relative p-0">
            <LoadingOverlay active={loading && tenants.length === 0} label="Loading tenants…" />
            {table.total === 0 && !loading ? (
              <EmptyState
                title={search ? "No tenants match that search." : "No tenants yet."}
                description={
                  search ? "Try a different name, slug, or status." : "Create your first tenant to get started."
                }
                className="py-12"
              />
            ) : (
              <>
                <DataTable
                  columns={columns}
                  rows={table.pageRows}
                  getRowId={(t) => t.id}
                  sort={table.sort}
                  onSortToggle={table.toggleSort}
                  loading={loading && tenants.length > 0}
                  stickyHeader
                />
                <Pagination
                  page={table.page}
                  pageSize={table.pageSize}
                  total={table.total}
                  onPageChange={table.setPage}
                  onPageSizeChange={table.setPageSize}
                />
              </>
            )}
          </CardContent>
        </Card>

      <TenantCreateDialog
        open={createOpen}
        onClose={() => setCreateOpen(false)}
        onCreated={async () => {
          setCreateOpen(false)
          await refresh()
        }}
        onError={setError}
      />
      <ConfirmDialog
        open={pending?.kind === "suspend"}
        onOpenChange={(o) => !o && setPending(null)}
        title="Suspend tenant?"
        description={
          pending
            ? `${pending.tenant.name} will be suspended. Members won't be able to sign in until you reactivate.`
            : ""
        }
        confirmLabel="Suspend"
        variant="default"
        onConfirm={() => runAction(pending)}
      />
      <ConfirmDialog
        open={pending?.kind === "deactivate"}
        onOpenChange={(o) => !o && setPending(null)}
        title="Deactivate tenant?"
        description={
          pending
            ? `${pending.tenant.name} will be deactivated. This is more severe than suspending.`
            : ""
        }
        confirmLabel="Deactivate"
        variant="danger"
        onConfirm={() => runAction(pending)}
      />
    </AppShell>
  )
}

function statusTone(status: TenantStatus): BadgeTone {
  if (status === "active") return "success"
  if (status === "suspended") return "warning"
  if (status === "deactivated") return "danger"
  return "default"
}

function rowActions(
  t: Tenant,
  arcadia: ReturnType<typeof useArcadiaClient>,
  refresh: () => Promise<void>,
  setPending: (p: PendingAction) => void,
  setError: (msg: string | null) => void,
): ActionItem[] {
  const items: ActionItem[] = []
  if (t.status === "active") {
    items.push({
      id: "suspend",
      label: "Suspend",
      icon: <Pause className="size-4" />,
      dataAction: `tenant-${t.slug}-suspend`,
      onSelect: () => setPending({ kind: "suspend", tenant: t }),
    })
  } else {
    items.push({
      id: "activate",
      label: "Activate",
      icon: <Play className="size-4" />,
      dataAction: `tenant-${t.slug}-activate`,
      onSelect: async () => {
        try {
          await activateTenant(arcadia, t.id)
          await refresh()
        } catch (err) {
          setError(err instanceof ArcadiaError ? err.message : "Activate failed.")
        }
      },
    })
  }
  items.push({
    id: "deactivate",
    label: "Deactivate",
    destructive: true,
    dataAction: `tenant-${t.slug}-deactivate`,
    onSelect: () => setPending({ kind: "deactivate", tenant: t }),
  })
  return items
}

function formatArcadiaError(err: unknown, fallback: string): string {
  if (!(err instanceof ArcadiaError)) return fallback
  // 422 validation errors carry per-field reasons in `details`. Shape from
  // Ecto's FallbackController is typically `{ field: ["msg1", "msg2"] }` or
  // nested `{ tenant: { slug: ["has already been taken"] } }`. Flatten so
  // the user sees what to fix instead of a generic "validation failed".
  if (err.isValidation && err.details) {
    const lines: string[] = []
    const walk = (obj: unknown, prefix: string) => {
      if (Array.isArray(obj)) {
        lines.push(`${prefix}: ${obj.join(", ")}`)
      } else if (obj && typeof obj === "object") {
        for (const [k, v] of Object.entries(obj)) {
          walk(v, prefix ? `${prefix}.${k}` : k)
        }
      }
    }
    walk(err.details, "")
    if (lines.length) return `${err.message} — ${lines.join("; ")}`
  }
  return err.message
}

function slugify(name: string): string {
  return name
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "")
}

function TenantCreateDialog({
  open,
  onClose,
  onCreated,
  onError,
}: {
  open: boolean
  onClose: () => void
  onCreated: () => Promise<void> | void
  onError: (msg: string) => void
}) {
  const arcadia = useArcadiaClient()
  const [name, setName] = useState("")
  const [slug, setSlug] = useState("")
  const [slugDirty, setSlugDirty] = useState(false)
  const [firstName, setFirstName] = useState("")
  const [lastName, setLastName] = useState("")
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  const [submitting, setSubmitting] = useState(false)

  useEffect(() => {
    if (!open) {
      setName("")
      setSlug("")
      setSlugDirty(false)
      setFirstName("")
      setLastName("")
      setEmail("")
      setPassword("")
      setSubmitting(false)
    }
  }, [open])

  const slugInvalid = slug.length > 0 && !/^[a-z0-9-]+$/.test(slug)
  const canSubmit =
    !submitting &&
    name.trim().length > 0 &&
    slug.length > 0 &&
    !slugInvalid &&
    firstName.trim().length > 0 &&
    lastName.trim().length > 0 &&
    email.trim().length > 0 &&
    password.length >= 8

  async function handleSubmit(e: FormEvent) {
    e.preventDefault()
    if (!canSubmit) return
    setSubmitting(true)
    try {
      await provisionTenant(arcadia, {
        tenant: { name: name.trim(), slug },
        admin_user: {
          email: email.trim(),
          password,
          first_name: firstName.trim(),
          last_name: lastName.trim(),
        },
      })
      await onCreated()
    } catch (err) {
      onError(formatArcadiaError(err, "Failed to create tenant."))
      setSubmitting(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-lg">
        <form onSubmit={handleSubmit}>
          <DialogHeader>
            <DialogTitle>New tenant</DialogTitle>
            <DialogDescription>
              Provisions the tenant with default roles, quotas, and an initial admin user.
            </DialogDescription>
          </DialogHeader>

          <div className="space-y-4 py-4">
            <div className="space-y-2">
              <Label htmlFor="tenant-name">Tenant name</Label>
              <Input
                id="tenant-name"
                value={name}
                onChange={(e) => {
                  setName(e.target.value)
                  if (!slugDirty) setSlug(slugify(e.target.value))
                }}
                placeholder="Acme Corp"
                autoFocus
                data-action="tenants-create-name"
              />
            </div>

            <div className="space-y-2">
              <Label htmlFor="tenant-slug">Slug</Label>
              <Input
                id="tenant-slug"
                value={slug}
                onChange={(e) => {
                  setSlugDirty(true)
                  setSlug(e.target.value)
                }}
                placeholder="acme"
                data-action="tenants-create-slug"
              />
              <p className="text-xs text-muted-foreground">
                {slugInvalid
                  ? "Lowercase letters, digits, and hyphens only."
                  : "Lowercase letters, digits, and hyphens. Used in URLs and the X-Tenant-ID header."}
              </p>
            </div>

            <div className="grid grid-cols-2 gap-3">
              <div className="space-y-2">
                <Label htmlFor="tenant-admin-first-name">Admin first name</Label>
                <Input
                  id="tenant-admin-first-name"
                  value={firstName}
                  onChange={(e) => setFirstName(e.target.value)}
                  placeholder="Jane"
                  data-action="tenants-create-admin-first-name"
                />
              </div>
              <div className="space-y-2">
                <Label htmlFor="tenant-admin-last-name">Admin last name</Label>
                <Input
                  id="tenant-admin-last-name"
                  value={lastName}
                  onChange={(e) => setLastName(e.target.value)}
                  placeholder="Doe"
                  data-action="tenants-create-admin-last-name"
                />
              </div>
            </div>

            <div className="space-y-2">
              <Label htmlFor="tenant-admin-email">Admin email</Label>
              <Input
                id="tenant-admin-email"
                type="email"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                placeholder="admin@acme.com"
                data-action="tenants-create-admin-email"
              />
            </div>

            <div className="space-y-2">
              <Label htmlFor="tenant-admin-password">Admin password</Label>
              <Input
                id="tenant-admin-password"
                type="password"
                value={password}
                onChange={(e) => setPassword(e.target.value)}
                placeholder="At least 8 characters"
                data-action="tenants-create-admin-password"
              />
            </div>
          </div>

          <DialogFooter>
            <Button
              type="button"
              variant="outline"
              onClick={onClose}
              disabled={submitting}
              data-action="tenants-create-cancel"
            >
              Cancel
            </Button>
            <Button
              type="submit"
              disabled={!canSubmit}
              data-action="tenants-create-submit"
            >
              {submitting ? "Creating…" : "Create tenant"}
            </Button>
          </DialogFooter>
        </form>
      </DialogContent>
    </Dialog>
  )
}
