import { FormEvent, useEffect, useMemo, useState } from "react";
import { CheckSquare, Network, RefreshCw, Router, Search, Square } from "lucide-react";

import { api } from "../api/client";
import { Button } from "../components/Button";
import type { SnmpCredentialProfile, SnmpDiscoveredInterface, SnmpDiscoveryItem, SnmpDiscoveryResult } from "../types/api";

interface DiscoveryPageProps {
  token: string;
}

export function DiscoveryPage({ token }: DiscoveryPageProps) {
  const [profiles, setProfiles] = useState<SnmpCredentialProfile[]>([]);
  const [host, setHost] = useState("");
  const [profileId, setProfileId] = useState<number | "">("");
  const [result, setResult] = useState<SnmpDiscoveryResult | null>(null);
  const [selectedItemIds, setSelectedItemIds] = useState<Set<string>>(new Set());
  const [loadingProfiles, setLoadingProfiles] = useState(false);
  const [discovering, setDiscovering] = useState(false);
  const [message, setMessage] = useState<string | null>(null);

  const selectedItems = useMemo(
    () => (result?.monitorable_items ?? []).filter((item) => selectedItemIds.has(item.item_id)),
    [result, selectedItemIds]
  );
  const groupedItems = useMemo(() => groupItems(result?.monitorable_items ?? []), [result]);

  async function refreshProfiles() {
    setLoadingProfiles(true);
    try {
      const nextProfiles = await api.snmpCredentialProfiles(token);
      setProfiles(nextProfiles);
      setProfileId((current) => current || nextProfiles[0]?.id || "");
    } finally {
      setLoadingProfiles(false);
    }
  }

  useEffect(() => {
    refreshProfiles().catch(() => setProfiles([]));
  }, [token]);

  async function submit(event: FormEvent) {
    event.preventDefault();
    if (!profileId) return;

    setDiscovering(true);
    setMessage(null);
    setResult(null);
    setSelectedItemIds(new Set());
    try {
      const discovered = await api.discoverSnmpDevice(token, {
        host,
        credential_profile_id: profileId,
      });
      setResult(discovered);
    } catch (err) {
      setMessage(err instanceof Error ? err.message : "SNMP discovery failed");
    } finally {
      setDiscovering(false);
    }
  }

  function toggleItem(itemId: string) {
    setSelectedItemIds((current) => {
      const next = new Set(current);
      if (next.has(itemId)) {
        next.delete(itemId);
      } else {
        next.add(itemId);
      }
      return next;
    });
  }

  function selectGroup(items: SnmpDiscoveryItem[]) {
    setSelectedItemIds((current) => {
      const next = new Set(current);
      const allSelected = items.every((item) => next.has(item.item_id));
      for (const item of items) {
        if (allSelected) {
          next.delete(item.item_id);
        } else {
          next.add(item.item_id);
        }
      }
      return next;
    });
  }

  return (
    <div className="space-y-6">
      <div className="flex flex-col justify-between gap-4 md:flex-row md:items-end">
        <div>
          <h1 className="text-3xl font-semibold">Discovery</h1>
          <p className="mt-2 text-sm text-slate-400">Find SNMP devices and choose friendly items for monitoring.</p>
        </div>
        <Button variant="ghost" onClick={refreshProfiles} disabled={loadingProfiles}>
          <RefreshCw size={16} />
          Refresh Profiles
        </Button>
      </div>

      <section className="grid gap-5 xl:grid-cols-[420px_minmax(0,1fr)]">
        <form className="space-y-4 rounded-md border border-line bg-[#0d131c] p-5" onSubmit={submit}>
          <div className="flex items-center gap-2">
            <Search size={18} className="text-pulse" />
            <h2 className="text-base font-semibold">SNMP Device Discovery</h2>
          </div>

          <label className="block space-y-2">
            <span className="text-sm text-slate-300">Device Host</span>
            <input className="h-10 w-full rounded-md border border-line bg-slate-950 px-3 text-sm outline-none ring-pulse/40 focus:ring-2" value={host} onChange={(event) => setHost(event.target.value)} placeholder="192.168.1.1 or switch.local" required />
          </label>

          <label className="block space-y-2">
            <span className="text-sm text-slate-300">SNMP Profile</span>
            <select className="h-10 w-full rounded-md border border-line bg-slate-950 px-3 text-sm outline-none ring-pulse/40 focus:ring-2" value={profileId} onChange={(event) => setProfileId(Number(event.target.value))} required>
              <option value="" disabled>
                Select a profile
              </option>
              {profiles.map((profile) => (
                <option key={profile.id} value={profile.id}>
                  {profile.name} - SNMPv{profile.version}
                </option>
              ))}
            </select>
          </label>

          {message ? <div className="rounded-md border border-red-500/40 bg-red-950/40 p-3 text-sm text-red-200">{message}</div> : null}

          {!profiles.length ? <div className="rounded-md border border-line bg-slate-950 p-3 text-sm text-slate-400">Add an SNMP credential profile before running discovery.</div> : null}

          <Button className="w-full" disabled={discovering || !profiles.length} type="submit">
            <Search size={16} />
            {discovering ? "Discovering..." : "Run Discovery"}
          </Button>
        </form>

        <div className="space-y-5">
          <div className="rounded-md border border-line bg-[#0d131c]">
            <div className="border-b border-line p-4">
              <h2 className="text-base font-semibold">Device Summary</h2>
            </div>
            {result ? (
              <div className="grid gap-4 p-4 md:grid-cols-3">
                <SummaryItem label="Name" value={result.device_name || result.host} />
                <SummaryItem label="Host" value={result.host} />
                <SummaryItem label="Profile" value={result.profile_name} />
                <SummaryItem label="Uptime" value={formatDuration(result.uptime_seconds)} />
                <SummaryItem label="Capabilities" value={formatCapabilities(result.capabilities)} />
                <div className="md:col-span-3">
                  <div className="text-xs uppercase text-slate-500">Description</div>
                  <div className="mt-1 text-sm text-slate-300">{result.description || "No description reported"}</div>
                </div>
              </div>
            ) : (
              <div className="p-6 text-sm text-slate-400">Run discovery to see device details.</div>
            )}
          </div>

          <div className="rounded-md border border-line bg-[#0d131c]">
            <div className="flex items-center gap-2 border-b border-line p-4">
              <Network size={18} className="text-pulse" />
              <h2 className="text-base font-semibold">Interfaces</h2>
            </div>
            <div className="divide-y divide-line">
              {result?.interfaces.length ? (
                result.interfaces.map((item) => (
                  <div key={item.index} className="grid gap-2 p-4 md:grid-cols-[1fr_120px_120px_120px] md:items-center">
                    <div>
                      <div className="font-medium">{item.label}</div>
                      <div className="text-sm text-slate-400">{interfaceContext(item)}</div>
                    </div>
                    <Status value={item.admin_status || "unknown"} />
                    <Status value={item.oper_status || "unknown"} />
                    <div className="text-sm text-slate-300">{formatSpeed(item.speed_bps)}</div>
                  </div>
                ))
              ) : (
                <div className="p-6 text-sm text-slate-400">{result ? "No interfaces reported." : "No discovery results yet."}</div>
              )}
            </div>
          </div>

          <div className="rounded-md border border-line bg-[#0d131c]">
            <div className="flex flex-col gap-2 border-b border-line p-4 md:flex-row md:items-center md:justify-between">
              <div className="flex items-center gap-2">
                <Router size={18} className="text-pulse" />
                <h2 className="text-base font-semibold">Monitorable Items</h2>
              </div>
              <div className="text-sm text-slate-400">{selectedItems.length} selected</div>
            </div>
            <div className="divide-y divide-line">
              {result ? (
                groupedItems.map(({ group, items }) => (
                  <div key={group} className="p-4">
                    <div className="mb-3 flex items-center justify-between gap-3">
                      <h3 className="text-sm font-semibold text-slate-200">{group}</h3>
                      <Button className="h-8 px-3" onClick={() => selectGroup(items)} type="button" variant="ghost">
                        {items.every((item) => selectedItemIds.has(item.item_id)) ? <CheckSquare size={15} /> : <Square size={15} />}
                        Group
                      </Button>
                    </div>
                    <div className="grid gap-2 md:grid-cols-2">
                      {items.map((item) => (
                        <button key={item.item_id} className="flex min-h-12 items-center gap-3 rounded-md border border-line bg-slate-950 px-3 py-2 text-left transition hover:bg-slate-900" onClick={() => toggleItem(item.item_id)} type="button">
                          {selectedItemIds.has(item.item_id) ? <CheckSquare className="shrink-0 text-pulse" size={18} /> : <Square className="shrink-0 text-slate-500" size={18} />}
                          <span className="min-w-0">
                            <span className="block text-sm font-medium text-slate-200">{item.label}</span>
                            <span className="block text-xs text-slate-500">{friendlyItemType(item.item_type)}{item.unit ? `, ${item.unit}` : ""}</span>
                          </span>
                        </button>
                      ))}
                    </div>
                  </div>
                ))
              ) : (
                <div className="p-6 text-sm text-slate-400">Run discovery to choose monitorable items.</div>
              )}
            </div>
          </div>
        </div>
      </section>
    </div>
  );
}

function groupItems(items: SnmpDiscoveryItem[]) {
  const groups = new Map<string, SnmpDiscoveryItem[]>();
  for (const item of items) {
    groups.set(item.group, [...(groups.get(item.group) ?? []), item]);
  }
  return Array.from(groups, ([group, groupItems]) => ({ group, items: groupItems }));
}

function SummaryItem({ label, value }: { label: string; value: string }) {
  return (
    <div>
      <div className="text-xs uppercase text-slate-500">{label}</div>
      <div className="mt-1 text-sm font-medium text-slate-200">{value}</div>
    </div>
  );
}

function Status({ value }: { value: string }) {
  const classes = value === "up" ? "border-teal-500/40 bg-teal-950/40 text-teal-200" : value === "down" ? "border-red-500/40 bg-red-950/40 text-red-200" : "border-slate-600 bg-slate-900 text-slate-300";
  return <span className={`inline-flex h-7 w-fit items-center rounded-md border px-2 text-xs font-medium ${classes}`}>{value}</span>;
}

function formatDuration(seconds?: number | null) {
  if (!seconds) return "Unknown";
  const days = Math.floor(seconds / 86400);
  const hours = Math.floor((seconds % 86400) / 3600);
  if (days) return `${days}d ${hours}h`;
  return `${hours}h ${Math.floor((seconds % 3600) / 60)}m`;
}

function formatSpeed(value?: number | null) {
  if (!value) return "Unknown speed";
  if (value >= 1_000_000_000) return `${value / 1_000_000_000} Gbps`;
  if (value >= 1_000_000) return `${value / 1_000_000} Mbps`;
  if (value >= 1_000) return `${value / 1_000} Kbps`;
  return `${value} bps`;
}

function friendlyItemType(value: string) {
  return value.replaceAll("_", " ");
}

function interfaceContext(item: SnmpDiscoveredInterface) {
  const details = [item.name, item.description].filter((value, index, values) => value && values.indexOf(value) === index);
  return details.length ? details.join(" - ") : "No description";
}

function formatCapabilities(capabilities: Record<string, boolean>) {
  const labels: Record<string, string> = {
    interfaces: "interfaces",
    cpu: "CPU",
    memory: "memory",
    storage: "storage",
    sensors: "sensors",
  };
  const active = Object.entries(labels)
    .filter(([key]) => capabilities[key])
    .map(([, label]) => label);
  return active.length ? active.join(", ") : "System only";
}
