import { useCallback, useEffect, useState } from "react"
import { Clock, ListChecks, Plus, RefreshCw, Trash2 } from "lucide-react"

import { useArcadiaClient } from "@crema/arcadia-core-client"
import { useToast } from "@crema/notification-ui"
import { ConfirmDialog, EmptyState } from "@crema/feedback-ui"
import { BadgeCell, type BadgeTone } from "@crema/table-ui"

import type { TenantTabProps } from "~/routes/tenants.$id"
import { DataState, DialogError } from "~/components/data-state"
import { errorMessage } from "~/lib/errors"
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 { Switch } from "~/components/ui/switch"
import {
  createInboundWebhook,
  deleteInboundWebhook,
  listInboundWebhookDeliveries,
  listInboundWebhooks,
  updateInboundWebhook,
  type InboundWebhookDelivery,
  type InboundWebhookSource,
} from "~/lib/arcadia/tenants"

/**
 * Inbound webhook sources: external providers whose signed callbacks this
 * tenant accepts and verifies. Each source can be toggled, deleted, and has a
 * recent-deliveries log.
 */
export function InboundWebhooksTab({ tenant }: TenantTabProps) {
  const arcadia = useArcadiaClient()
  const toast = useToast()

  const [sources, setSources] = useState<InboundWebhookSource[]>([])
  const [loading, setLoading] = useState(true)
  // Raw thrown value; DataState normalises it. A failed load must never render
  // as "no sources yet".
  const [error, setError] = useState<unknown>(null)
  const [busy, setBusy] = useState<Set<string>>(new Set())
  const [addOpen, setAddOpen] = useState(false)
  const [deliveriesFor, setDeliveriesFor] = useState<InboundWebhookSource | null>(null)
  const [pendingDelete, setPendingDelete] = useState<InboundWebhookSource | null>(null)

  const load = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      setSources(await listInboundWebhooks(arcadia, tenant.id))
    } catch (err) {
      setError(err)
    } finally {
      setLoading(false)
    }
  }, [arcadia, tenant.id])

  useEffect(() => {
    load()
  }, [load])

  const markBusy = (id: string, on: boolean) =>
    setBusy((prev) => {
      const next = new Set(prev)
      if (on) next.add(id)
      else next.delete(id)
      return next
    })

  const toggle = useCallback(
    async (src: InboundWebhookSource, next: boolean) => {
      markBusy(src.id, true)
      setSources((prev) =>
        prev.map((s) => (s.id === src.id ? { ...s, enabled: next } : s)),
      )
      try {
        const updated = await updateInboundWebhook(arcadia, tenant.id, src.id, {
          enabled: next,
        })
        setSources((prev) => prev.map((s) => (s.id === updated.id ? updated : s)))
        toast.success(`${next ? "Enabled" : "Disabled"} ${src.name}`)
      } catch (err) {
        setSources((prev) =>
          prev.map((s) => (s.id === src.id ? { ...s, enabled: !next } : s)),
        )
        toast.error(errorMessage(err, `update ${src.name}`))
      } finally {
        markBusy(src.id, false)
      }
    },
    [arcadia, tenant.id, toast],
  )

  return (
    <Card>
      <CardHeader className="flex flex-row items-start justify-between gap-4">
        <div className="space-y-1">
          <CardTitle>Inbound webhooks</CardTitle>
          <CardDescription>
            External providers whose signed callbacks {tenant.name} accepts and
            verifies.
          </CardDescription>
        </div>
        <div className="flex items-center gap-2">
          <Button
            variant="outline"
            size="sm"
            onClick={load}
            disabled={loading}
            data-action="tenant-detail-webhooks-refresh"
          >
            <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
            Refresh
          </Button>
          <Button
            size="sm"
            onClick={() => setAddOpen(true)}
            data-action="tenant-detail-webhooks-add"
          >
            <Plus className="size-4" />
            Add source
          </Button>
        </div>
      </CardHeader>

      <CardContent className="relative p-0">
        <DataState
          loading={loading}
          error={error}
          isEmpty={sources.length === 0}
          onRetry={load}
          loadingLabel="Loading webhook sources…"
          empty={
            <EmptyState
              title="No inbound webhook sources."
              description="Add one to accept and verify signed callbacks from an external provider."
              className="py-12"
            />
          }
        >
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b text-left text-xs uppercase tracking-wider text-muted-foreground">
                  <th className="px-6 py-2 font-medium">Name</th>
                  <th className="px-4 py-2 font-medium">Provider</th>
                  <th className="px-4 py-2 font-medium">Enabled</th>
                  <th className="px-6 py-2" />
                </tr>
              </thead>
              <tbody className="divide-y">
                {sources.map((src) => {
                  const isBusy = busy.has(src.id)
                  return (
                    <tr key={src.id}>
                      <td className="px-6 py-3 font-medium">{src.name}</td>
                      <td className="px-4 py-3 text-muted-foreground">
                        {src.provider || "—"}
                      </td>
                      <td className="px-4 py-3">
                        <Switch
                          checked={src.enabled}
                          disabled={isBusy}
                          onCheckedChange={(next) => toggle(src, next)}
                          data-action={`tenant-detail-webhooks-toggle-${src.id}`}
                        />
                      </td>
                      <td className="px-6 py-3">
                        <div className="flex items-center justify-end gap-1">
                          <Button
                            variant="outline"
                            size="sm"
                            onClick={() => setDeliveriesFor(src)}
                            data-action={`tenant-detail-webhooks-deliveries-${src.id}`}
                          >
                            <ListChecks className="size-4" />
                            Deliveries
                          </Button>
                          <Button
                            variant="ghost"
                            size="icon-sm"
                            className="text-destructive"
                            disabled={isBusy}
                            onClick={() => setPendingDelete(src)}
                            aria-label={`Delete ${src.name}`}
                            data-action={`tenant-detail-webhooks-delete-${src.id}`}
                          >
                            <Trash2 className="size-4" />
                          </Button>
                        </div>
                      </td>
                    </tr>
                  )
                })}
              </tbody>
            </table>
          </div>
        </DataState>
      </CardContent>

      <AddWebhookDialog
        open={addOpen}
        tenantId={tenant.id}
        onClose={() => setAddOpen(false)}
        onSaved={async (msg) => {
          setAddOpen(false)
          await load()
          toast.success(msg)
        }}
      />

      <DeliveriesDialog
        source={deliveriesFor}
        tenantId={tenant.id}
        onClose={() => setDeliveriesFor(null)}
      />

      <ConfirmDialog
        open={pendingDelete !== null}
        onOpenChange={(o) => !o && setPendingDelete(null)}
        title="Delete webhook source?"
        description={
          pendingDelete
            ? `${pendingDelete.name} will be removed. Incoming callbacks from this provider will be rejected, and its delivery history is discarded.`
            : ""
        }
        confirmLabel="Delete"
        variant="danger"
        onConfirm={async () => {
          if (!pendingDelete) return
          const target = pendingDelete
          try {
            await deleteInboundWebhook(arcadia, tenant.id, target.id)
            setPendingDelete(null)
            await load()
            toast.success(`Deleted ${target.name}`)
          } catch (err) {
            setPendingDelete(null)
            toast.error(errorMessage(err, `delete ${target.name}`))
          }
        }}
      />
    </Card>
  )
}

function AddWebhookDialog({
  open,
  tenantId,
  onClose,
  onSaved,
}: {
  open: boolean
  tenantId: string
  onClose: () => void
  onSaved: (message: string) => Promise<void>
}) {
  const arcadia = useArcadiaClient()
  const [name, setName] = useState("")
  const [provider, setProvider] = useState("")
  const [signingSecret, setSigningSecret] = useState("")
  const [signatureHeader, setSignatureHeader] = useState("")
  const [signatureAlgorithm, setSignatureAlgorithm] = useState("hmac_sha256")
  const [enabled, setEnabled] = useState(true)
  const [saving, setSaving] = useState(false)
  // Failed submit renders here, above the buttons, with the form intact.
  const [error, setError] = useState<unknown>(null)

  useEffect(() => {
    if (!open) return
    setName("")
    setProvider("")
    setSigningSecret("")
    setSignatureHeader("")
    setSignatureAlgorithm("hmac_sha256")
    setEnabled(true)
    setError(null)
  }, [open])

  const submit = async () => {
    const trimmedName = name.trim()
    setError(null)
    setSaving(true)
    try {
      if (!trimmedName) throw new Error("A name is required.")
      await createInboundWebhook(arcadia, tenantId, {
        name: trimmedName,
        provider: provider.trim() || undefined,
        signing_secret: signingSecret || undefined,
        signature_header: signatureHeader.trim() || undefined,
        signature_algorithm: signatureAlgorithm.trim() || undefined,
        enabled,
      })
      await onSaved(`Added webhook source ${trimmedName}`)
    } catch (err) {
      setError(err)
    } finally {
      setSaving(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-lg max-h-[90vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle>Add webhook source</DialogTitle>
          <DialogDescription>
            Register an external provider whose signed callbacks this tenant will
            accept and verify.
          </DialogDescription>
        </DialogHeader>

        <div className="flex flex-col gap-4">
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="webhook-name">Name</Label>
            <Input
              id="webhook-name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="Stripe events"
              autoFocus
              data-action="tenant-detail-webhooks-form-name"
            />
          </div>

          <div className="flex flex-col gap-1.5">
            <Label htmlFor="webhook-provider">Provider</Label>
            <Input
              id="webhook-provider"
              value={provider}
              onChange={(e) => setProvider(e.target.value)}
              placeholder="stripe"
              data-action="tenant-detail-webhooks-form-provider"
            />
          </div>

          <div className="flex flex-col gap-1.5">
            <Label htmlFor="webhook-secret">Signing secret</Label>
            <Input
              id="webhook-secret"
              type="password"
              value={signingSecret}
              onChange={(e) => setSigningSecret(e.target.value)}
              placeholder="whsec_…"
              data-action="tenant-detail-webhooks-form-secret"
            />
            <p className="text-xs text-muted-foreground">
              Stored encrypted; used to verify incoming signatures. Write-only.
            </p>
          </div>

          <div className="grid grid-cols-2 gap-3">
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="webhook-header">Signature header</Label>
              <Input
                id="webhook-header"
                value={signatureHeader}
                onChange={(e) => setSignatureHeader(e.target.value)}
                placeholder="X-Signature"
                className="font-mono"
                data-action="tenant-detail-webhooks-form-header"
              />
            </div>
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="webhook-algorithm">Signature algorithm</Label>
              <Input
                id="webhook-algorithm"
                value={signatureAlgorithm}
                onChange={(e) => setSignatureAlgorithm(e.target.value)}
                placeholder="hmac_sha256"
                className="font-mono"
                data-action="tenant-detail-webhooks-form-algorithm"
              />
            </div>
          </div>

          <div className="flex items-center justify-between rounded-md border px-3 py-2">
            <div>
              <div className="text-sm font-medium">Enabled</div>
              <div className="text-xs text-muted-foreground">
                Disabled sources reject incoming callbacks.
              </div>
            </div>
            <Switch
              checked={enabled}
              onCheckedChange={setEnabled}
              data-action="tenant-detail-webhooks-form-enabled"
            />
          </div>
        </div>

        {error ? <DialogError error={error} context="add the source" /> : null}

        <DialogFooter>
          <Button
            variant="outline"
            onClick={onClose}
            disabled={saving}
            data-action="tenant-detail-webhooks-form-cancel"
          >
            Cancel
          </Button>
          <Button
            onClick={submit}
            disabled={saving || !name.trim()}
            data-action="tenant-detail-webhooks-form-save"
          >
            {saving ? <RefreshCw className="size-4 animate-spin" /> : null}
            Add source
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

function DeliveriesDialog({
  source,
  tenantId,
  onClose,
}: {
  source: InboundWebhookSource | null
  tenantId: string
  onClose: () => void
}) {
  const arcadia = useArcadiaClient()
  const [deliveries, setDeliveries] = useState<InboundWebhookDelivery[]>([])
  const [loading, setLoading] = useState(true)
  // A deliveries load that failed is not a source with no history. Own error
  // state, rendered in place of the list.
  const [error, setError] = useState<unknown>(null)
  const [reloadKey, setReloadKey] = useState(0)

  useEffect(() => {
    if (!source) return
    let mounted = true
    setLoading(true)
    setError(null)
    listInboundWebhookDeliveries(arcadia, tenantId, source.id)
      .then((rows) => {
        if (mounted) setDeliveries(rows)
      })
      .catch((err) => {
        if (mounted) setError(err)
      })
      .finally(() => {
        if (mounted) setLoading(false)
      })
    return () => {
      mounted = false
    }
  }, [arcadia, tenantId, source, reloadKey])

  return (
    <Dialog open={source !== null} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-lg">
        <DialogHeader>
          <DialogTitle>Deliveries{source ? ` — ${source.name}` : ""}</DialogTitle>
          <DialogDescription>
            Recent signed callbacks received from this provider.
          </DialogDescription>
        </DialogHeader>

        <DataState
          loading={loading}
          error={error}
          isEmpty={deliveries.length === 0}
          onRetry={() => setReloadKey((n) => n + 1)}
          loadingLabel="Loading deliveries…"
          empty={
            <p className="py-8 text-center text-sm text-muted-foreground">
              No deliveries yet. Callbacks this source receives and verifies will
              appear here.
            </p>
          }
        >
          <ul className="flex max-h-[50vh] flex-col divide-y overflow-y-auto rounded-md border">
            {deliveries.map((d) => {
              const when = d.received_at ?? d.inserted_at
              return (
                <li
                  key={d.id}
                  className="flex items-center justify-between gap-3 px-3 py-2"
                >
                  <BadgeCell
                    label={d.status ?? "unknown"}
                    tone={deliveryTone(d.status)}
                  />
                  <span className="text-xs text-muted-foreground">
                    <Clock className="mr-1 inline size-3" />
                    {when ? new Date(when).toLocaleString() : "—"}
                  </span>
                </li>
              )
            })}
          </ul>
        </DataState>

        <DialogFooter>
          <Button
            variant="outline"
            onClick={onClose}
            data-action="tenant-detail-webhooks-deliveries-close"
          >
            Close
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  )
}

function deliveryTone(status?: string): BadgeTone {
  const s = (status ?? "").toLowerCase()
  if (["ok", "success", "delivered", "verified", "processed"].includes(s))
    return "success"
  if (["failed", "error", "rejected", "invalid"].includes(s)) return "danger"
  if (["pending", "retrying", "queued"].includes(s)) return "warning"
  return "default"
}
