import { useCallback, useEffect, useState } from "react"
import {
  CheckCircle2,
  Globe,
  Network,
  Plus,
  RefreshCw,
  Shield,
  Trash2,
  Wifi,
} from "lucide-react"

import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-core-client"
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 { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
import {
  assignFloatingIp,
  createDnsRecord,
  deleteDnsRecord,
  deleteFirewall,
  listDnsRecords,
  listDomains,
  listFirewalls,
  listFloatingIps,
  listVpcs,
  unassignFloatingIp,
  type DnsRecord,
  type Domain,
  type Firewall,
  type FloatingIp,
  type Vpc,
} from "~/lib/arcadia/networking"
import { listDroplets, type Droplet } from "~/lib/arcadia/monitoring"
import { pageTitle } from "~/lib/page-meta"
import { useSession } from "~/lib/session"
import { useRegisterContext } from "@crema/aifirst-ui/context"

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

const DNS_TYPES = ["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA"]

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

  const [firewalls, setFirewalls] = useState<Firewall[]>([])
  const [vpcs, setVpcs] = useState<Vpc[]>([])
  const [domains, setDomains] = useState<Domain[]>([])
  const [floatingIps, setFloatingIps] = useState<FloatingIp[]>([])
  const [droplets, setDroplets] = useState<Droplet[]>([])
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [info, setInfo] = useState<string | null>(null)

  const refresh = useCallback(async () => {
    setError(null)
    setLoading(true)
    try {
      const [f, v, d, fi, dr] = await Promise.all([
        listFirewalls(arcadia),
        listVpcs(arcadia),
        listDomains(arcadia),
        listFloatingIps(arcadia),
        listDroplets(arcadia),
      ])
      setFirewalls(f)
      setVpcs(v)
      setDomains(d)
      setFloatingIps(fi)
      setDroplets(dr)
    } catch (err) {
      setError(err instanceof ArcadiaError ? err.message : "Failed to load networking.")
    } finally {
      setLoading(false)
    }
  }, [arcadia])

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

  useRegisterContext("networking", {
    firewalls: firewalls.length,
    vpcs: vpcs.length,
    domains: domains.length,
    floating_ips: floatingIps.length,
    droplets: droplets.length,
  })

  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">Networking</h1>
            <p className="text-sm text-muted-foreground">
              Firewalls, VPCs, DNS, and floating IPs on the platform's underlying provider.
            </p>
          </div>
          <Button
            variant="outline"
            size="sm"
            onClick={refresh}
            disabled={loading}
            data-action="networking-refresh"
          >
            <RefreshCw className={`size-4 ${loading ? "animate-spin" : ""}`} />
            Refresh
          </Button>
        </header>

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

        <Tabs defaultValue="firewalls">
          <TabsList>
            <TabsTrigger value="firewalls" data-action="networking-tab-firewalls">
              Firewalls ({firewalls.length})
            </TabsTrigger>
            <TabsTrigger value="vpcs" data-action="networking-tab-vpcs">
              VPCs ({vpcs.length})
            </TabsTrigger>
            <TabsTrigger value="domains" data-action="networking-tab-domains">
              DNS ({domains.length})
            </TabsTrigger>
            <TabsTrigger value="floating-ips" data-action="networking-tab-floating-ips">
              Floating IPs ({floatingIps.length})
            </TabsTrigger>
          </TabsList>

          <TabsContent value="firewalls" className="pt-4">
            <FirewallsPanel
              firewalls={firewalls}
              loading={loading}
              onChanged={refresh}
              onError={setError}
              onInfo={setInfo}
            />
          </TabsContent>

          <TabsContent value="vpcs" className="pt-4">
            <VpcsPanel vpcs={vpcs} loading={loading} />
          </TabsContent>

          <TabsContent value="domains" className="pt-4">
            <DomainsPanel
              domains={domains}
              loading={loading}
              onError={setError}
              onInfo={setInfo}
              onChanged={refresh}
            />
          </TabsContent>

          <TabsContent value="floating-ips" className="pt-4">
            <FloatingIpsPanel
              ips={floatingIps}
              droplets={droplets}
              loading={loading}
              onChanged={refresh}
              onError={setError}
              onInfo={setInfo}
            />
          </TabsContent>
        </Tabs>
      </div>
    </AppShell>
  )
}

// --- Firewalls panel ---------------------------------------------------

function FirewallsPanel({
  firewalls,
  loading,
  onChanged,
  onError,
  onInfo,
}: {
  firewalls: Firewall[]
  loading: boolean
  onChanged: () => Promise<void>
  onError: (msg: string | null) => void
  onInfo: (msg: string | null) => void
}) {
  const arcadia = useArcadiaClient()
  const [pendingDelete, setPendingDelete] = useState<Firewall | null>(null)

  if (loading && firewalls.length === 0) {
    return (
      <Card>
        <CardContent className="relative py-8">
          <LoadingOverlay active label="Loading firewalls…" />
        </CardContent>
      </Card>
    )
  }

  if (firewalls.length === 0) {
    return (
      <Card>
        <CardContent>
          <EmptyState
            icon={<Shield className="size-6" />}
            title="No firewalls."
            description="Create a firewall on your provider, or configure DigitalOcean access in arcadia's .env to see existing ones."
            className="py-8"
          />
        </CardContent>
      </Card>
    )
  }

  return (
    <>
      <ul className="grid grid-cols-1 gap-3 lg:grid-cols-2">
        {firewalls.map((f) => (
          <Card key={String(f.id)}>
            <CardHeader className="flex flex-row items-center justify-between gap-3">
              <div className="flex items-center gap-2">
                <Shield className="size-4 text-muted-foreground" />
                <CardTitle className="text-base">{f.name}</CardTitle>
                {f.status ? <Badge variant="secondary">{f.status}</Badge> : null}
              </div>
              <Button
                variant="ghost"
                size="sm"
                onClick={() => setPendingDelete(f)}
                data-action={`firewall-${f.id}-delete`}
              >
                <Trash2 className="size-3.5" />
              </Button>
            </CardHeader>
            <CardContent className="text-xs text-muted-foreground">
              Inbound rules: {f.inbound_rules?.length ?? 0} · Outbound rules:{" "}
              {f.outbound_rules?.length ?? 0} · Droplets attached:{" "}
              {f.droplet_ids?.length ?? 0}
            </CardContent>
          </Card>
        ))}
      </ul>

      <ConfirmDialog
        open={pendingDelete !== null}
        onOpenChange={(o) => !o && setPendingDelete(null)}
        title="Delete firewall?"
        description={
          pendingDelete
            ? `${pendingDelete.name} will be removed. Attached droplets lose this rule set.`
            : ""
        }
        confirmLabel="Delete"
        variant="danger"
        onConfirm={async () => {
          if (!pendingDelete) return
          try {
            await deleteFirewall(arcadia, pendingDelete.id)
            setPendingDelete(null)
            onInfo("Firewall deleted.")
            await onChanged()
          } catch (err) {
            onError(err instanceof ArcadiaError ? err.message : "Delete failed.")
            setPendingDelete(null)
          }
        }}
      />
    </>
  )
}

// --- VPCs panel --------------------------------------------------------

function VpcsPanel({ vpcs, loading }: { vpcs: Vpc[]; loading: boolean }) {
  if (loading && vpcs.length === 0) {
    return (
      <Card>
        <CardContent className="relative py-8">
          <LoadingOverlay active label="Loading VPCs…" />
        </CardContent>
      </Card>
    )
  }
  if (vpcs.length === 0) {
    return (
      <Card>
        <CardContent>
          <EmptyState
            icon={<Network className="size-6" />}
            title="No VPCs."
            description="Read-only view; create VPCs on your provider directly."
            className="py-8"
          />
        </CardContent>
      </Card>
    )
  }
  return (
    <ul className="grid grid-cols-1 gap-3 lg:grid-cols-2">
      {vpcs.map((v) => (
        <Card key={v.id}>
          <CardHeader className="flex flex-row items-center justify-between">
            <div className="flex items-center gap-2">
              <Network className="size-4 text-muted-foreground" />
              <CardTitle className="text-base">{v.name}</CardTitle>
              {v.default ? <Badge>default</Badge> : null}
            </div>
          </CardHeader>
          <CardContent className="text-xs text-muted-foreground">
            <div>
              Region: <code className="font-mono">{v.region ?? "—"}</code>
            </div>
            <div>
              IP range: <code className="font-mono">{v.ip_range ?? "—"}</code>
            </div>
          </CardContent>
        </Card>
      ))}
    </ul>
  )
}

// --- Domains + DNS records panel ---------------------------------------

function DomainsPanel({
  domains,
  loading,
  onError,
  onInfo,
  onChanged,
}: {
  domains: Domain[]
  loading: boolean
  onError: (msg: string | null) => void
  onInfo: (msg: string | null) => void
  onChanged: () => Promise<void>
}) {
  const arcadia = useArcadiaClient()
  const [selectedName, setSelectedName] = useState<string>(() => domains[0]?.name ?? "")
  const [records, setRecords] = useState<DnsRecord[]>([])
  const [loadingRecords, setLoadingRecords] = useState(false)
  const [createOpen, setCreateOpen] = useState(false)
  const [pendingDelete, setPendingDelete] = useState<DnsRecord | null>(null)

  useEffect(() => {
    if (!selectedName && domains.length > 0) setSelectedName(domains[0].name)
  }, [domains, selectedName])

  const loadRecords = useCallback(
    async (name: string) => {
      if (!name) {
        setRecords([])
        return
      }
      setLoadingRecords(true)
      try {
        setRecords(await listDnsRecords(arcadia, name))
      } catch (err) {
        onError(err instanceof ArcadiaError ? err.message : "Failed to load DNS records.")
      } finally {
        setLoadingRecords(false)
      }
    },
    [arcadia, onError],
  )

  useEffect(() => {
    loadRecords(selectedName)
  }, [selectedName, loadRecords])

  if (loading && domains.length === 0) {
    return (
      <Card>
        <CardContent className="relative py-8">
          <LoadingOverlay active label="Loading domains…" />
        </CardContent>
      </Card>
    )
  }

  if (domains.length === 0) {
    return (
      <Card>
        <CardContent>
          <EmptyState
            icon={<Globe className="size-6" />}
            title="No domains."
            description="Add a domain on your provider; arcadia surfaces it here for record management."
            className="py-8"
          />
        </CardContent>
      </Card>
    )
  }

  return (
    <Card>
      <CardHeader className="flex flex-row flex-wrap items-end gap-3">
        <div className="flex flex-col gap-1.5">
          <Label htmlFor="dns-domain" className="text-xs">
            Domain
          </Label>
          <Select value={selectedName} onValueChange={setSelectedName}>
            <SelectTrigger id="dns-domain" className="w-64" data-action="dns-domain-select">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {domains.map((d) => (
                <SelectItem key={d.name} value={d.name}>
                  {d.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
        <div className="ml-auto flex gap-2">
          <Button
            variant="outline"
            size="sm"
            onClick={() => loadRecords(selectedName)}
            disabled={loadingRecords}
            data-action="dns-refresh"
          >
            <RefreshCw className={`size-4 ${loadingRecords ? "animate-spin" : ""}`} />
            Refresh
          </Button>
          <Button
            size="sm"
            onClick={() => setCreateOpen(true)}
            disabled={!selectedName}
            data-action="dns-create"
          >
            <Plus className="size-4" />
            New record
          </Button>
        </div>
      </CardHeader>
      <CardContent className="p-0">
        {records.length === 0 && !loadingRecords ? (
          <EmptyState
            icon={<Globe className="size-6" />}
            title="No records on this domain."
            className="py-8"
          />
        ) : (
          <ul className="divide-y border-y">
            {records.map((r) => (
              <li key={String(r.id)} className="flex items-center justify-between gap-3 px-3 py-2 text-sm">
                <div className="flex items-center gap-3">
                  <Badge variant="secondary" className="font-mono text-xs">
                    {r.type}
                  </Badge>
                  <span className="font-mono text-xs">{r.name}</span>
                  <span className="text-xs text-muted-foreground">→</span>
                  <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">
                    {r.data}
                  </code>
                  {r.ttl ? (
                    <span className="text-[11px] text-muted-foreground">TTL {r.ttl}s</span>
                  ) : null}
                </div>
                <Button
                  variant="ghost"
                  size="sm"
                  onClick={() => setPendingDelete(r)}
                  data-action={`dns-record-${r.id}-delete`}
                >
                  <Trash2 className="size-3.5" />
                </Button>
              </li>
            ))}
          </ul>
        )}
      </CardContent>

      <DnsCreateDialog
        open={createOpen}
        domainName={selectedName}
        onClose={() => setCreateOpen(false)}
        onCreated={async () => {
          setCreateOpen(false)
          onInfo("DNS record created.")
          await loadRecords(selectedName)
          await onChanged()
        }}
        onError={onError}
      />

      <ConfirmDialog
        open={pendingDelete !== null}
        onOpenChange={(o) => !o && setPendingDelete(null)}
        title="Delete DNS record?"
        description={
          pendingDelete
            ? `${pendingDelete.type} ${pendingDelete.name} → ${pendingDelete.data}. This is destructive and may break traffic.`
            : ""
        }
        confirmLabel="Delete"
        variant="danger"
        onConfirm={async () => {
          if (!pendingDelete) return
          try {
            await deleteDnsRecord(arcadia, selectedName, pendingDelete.id)
            setPendingDelete(null)
            onInfo("Record deleted.")
            await loadRecords(selectedName)
          } catch (err) {
            onError(err instanceof ArcadiaError ? err.message : "Delete failed.")
            setPendingDelete(null)
          }
        }}
      />
    </Card>
  )
}

function DnsCreateDialog({
  open,
  domainName,
  onClose,
  onCreated,
  onError,
}: {
  open: boolean
  domainName: string
  onClose: () => void
  onCreated: () => Promise<void>
  onError: (msg: string | null) => void
}) {
  const arcadia = useArcadiaClient()
  const [type, setType] = useState("A")
  const [name, setName] = useState("@")
  const [data, setData] = useState("")
  const [ttl, setTtl] = useState("3600")
  const [priority, setPriority] = useState("")
  const [saving, setSaving] = useState(false)

  useEffect(() => {
    if (!open) {
      setType("A")
      setName("@")
      setData("")
      setTtl("3600")
      setPriority("")
    }
  }, [open])

  const submit = async () => {
    onError(null)
    setSaving(true)
    try {
      await createDnsRecord(arcadia, domainName, {
        type,
        name,
        data,
        ttl: ttl ? Number(ttl) : undefined,
        priority: priority ? Number(priority) : undefined,
      })
      await onCreated()
    } catch (err) {
      onError(err instanceof ArcadiaError ? err.message : "Create failed.")
    } finally {
      setSaving(false)
    }
  }

  return (
    <Dialog open={open} onOpenChange={(o) => !o && onClose()}>
      <DialogContent className="sm:max-w-md">
        <DialogHeader>
          <DialogTitle>New DNS record</DialogTitle>
          <DialogDescription>
            On <code className="font-mono">{domainName}</code>.
          </DialogDescription>
        </DialogHeader>

        <div className="grid grid-cols-2 gap-3">
          <div className="flex flex-col gap-1.5">
            <Label>Type</Label>
            <Select value={type} onValueChange={setType}>
              <SelectTrigger data-action="dns-form-type">
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                {DNS_TYPES.map((t) => (
                  <SelectItem key={t} value={t}>
                    {t}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="dns-name">Name</Label>
            <Input
              id="dns-name"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="@ or sub"
              data-action="dns-form-name"
            />
          </div>
          <div className="col-span-2 flex flex-col gap-1.5">
            <Label htmlFor="dns-data">Data</Label>
            <Input
              id="dns-data"
              value={data}
              onChange={(e) => setData(e.target.value)}
              placeholder={
                type === "A"
                  ? "1.2.3.4"
                  : type === "CNAME"
                    ? "target.example.com."
                    : type === "TXT"
                      ? '"verification=..."'
                      : "value"
              }
              className="font-mono"
              data-action="dns-form-data"
            />
          </div>
          <div className="flex flex-col gap-1.5">
            <Label htmlFor="dns-ttl">TTL (seconds)</Label>
            <Input
              id="dns-ttl"
              type="number"
              min={30}
              value={ttl}
              onChange={(e) => setTtl(e.target.value)}
              data-action="dns-form-ttl"
            />
          </div>
          {type === "MX" || type === "SRV" ? (
            <div className="flex flex-col gap-1.5">
              <Label htmlFor="dns-priority">Priority</Label>
              <Input
                id="dns-priority"
                type="number"
                value={priority}
                onChange={(e) => setPriority(e.target.value)}
                data-action="dns-form-priority"
              />
            </div>
          ) : null}
        </div>

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

// --- Floating IPs panel ------------------------------------------------

function FloatingIpsPanel({
  ips,
  droplets,
  loading,
  onChanged,
  onError,
  onInfo,
}: {
  ips: FloatingIp[]
  droplets: Droplet[]
  loading: boolean
  onChanged: () => Promise<void>
  onError: (msg: string | null) => void
  onInfo: (msg: string | null) => void
}) {
  const arcadia = useArcadiaClient()
  const [assigning, setAssigning] = useState<{ ip: string; dropletId: string } | null>(null)

  if (loading && ips.length === 0) {
    return (
      <Card>
        <CardContent className="relative py-8">
          <LoadingOverlay active label="Loading floating IPs…" />
        </CardContent>
      </Card>
    )
  }
  if (ips.length === 0) {
    return (
      <Card>
        <CardContent>
          <EmptyState
            icon={<Wifi className="size-6" />}
            title="No floating IPs."
            description="Reserve a floating IP on your provider to surface it here."
            className="py-8"
          />
        </CardContent>
      </Card>
    )
  }

  return (
    <Card>
      <CardContent className="p-0">
        <ul className="divide-y border-y">
          {ips.map((ip) => {
            const region =
              typeof ip.region === "string" ? ip.region : ip.region?.slug ?? "—"
            return (
              <li key={ip.ip} className="flex items-center justify-between gap-3 px-3 py-2">
                <div className="flex items-center gap-3">
                  <Wifi className="size-4 text-muted-foreground" />
                  <code className="font-mono text-sm">{ip.ip}</code>
                  <span className="text-xs text-muted-foreground">{region}</span>
                  {ip.droplet ? (
                    <Badge variant="secondary">→ {ip.droplet.name ?? ip.droplet.id}</Badge>
                  ) : (
                    <Badge>unassigned</Badge>
                  )}
                </div>
                <div className="flex gap-2">
                  {ip.droplet ? (
                    <Button
                      variant="outline"
                      size="sm"
                      onClick={async () => {
                        try {
                          await unassignFloatingIp(arcadia, ip.ip)
                          onInfo("Floating IP unassigned.")
                          await onChanged()
                        } catch (err) {
                          onError(
                            err instanceof ArcadiaError ? err.message : "Unassign failed.",
                          )
                        }
                      }}
                      data-action={`fip-${ip.ip}-unassign`}
                    >
                      Unassign
                    </Button>
                  ) : (
                    <>
                      <Select
                        value={assigning?.ip === ip.ip ? assigning.dropletId : ""}
                        onValueChange={(v) => setAssigning({ ip: ip.ip, dropletId: v })}
                      >
                        <SelectTrigger
                          className="h-8 w-44"
                          data-action={`fip-${ip.ip}-droplet-select`}
                        >
                          <SelectValue placeholder="Pick droplet" />
                        </SelectTrigger>
                        <SelectContent>
                          {droplets.length === 0 ? (
                            <SelectItem value="__none" disabled>
                              No droplets
                            </SelectItem>
                          ) : (
                            droplets.map((d) => (
                              <SelectItem key={String(d.id)} value={String(d.id)}>
                                {d.name}
                              </SelectItem>
                            ))
                          )}
                        </SelectContent>
                      </Select>
                      <Button
                        size="sm"
                        disabled={
                          !assigning || assigning.ip !== ip.ip || !assigning.dropletId
                        }
                        onClick={async () => {
                          if (!assigning || assigning.ip !== ip.ip) return
                          try {
                            await assignFloatingIp(arcadia, ip.ip, assigning.dropletId)
                            setAssigning(null)
                            onInfo("Floating IP assigned.")
                            await onChanged()
                          } catch (err) {
                            onError(
                              err instanceof ArcadiaError ? err.message : "Assign failed.",
                            )
                          }
                        }}
                        data-action={`fip-${ip.ip}-assign`}
                      >
                        Assign
                      </Button>
                    </>
                  )}
                </div>
              </li>
            )
          })}
        </ul>
      </CardContent>
    </Card>
  )
}
