import { useMemo, useState } from "react";
import { useQuery } from "@tanstack/react-query";
import { Activity, ArrowRight, CircuitBoard, Hash, Network, Shield, ShieldCheck } from "lucide-react";
import { Link, useNavigate, useParams } from "react-router-dom";

import { api, Workload, WorkloadInsight } from "../api/client";
import { DataTable } from "../components/DataTable";
import { secondaryButtonClass } from "../components/FormControls";
import { PageHeader } from "../components/PageHeader";

type TrafficSummary = {
  key: string;
  source: string;
  destination: string;
  sourceLabel: string;
  destinationLabel: string;
  protocol: string;
  port: string;
  bytes: number;
  packets: number;
  count: number;
  decision: string;
  interfaceName: string;
  note: string;
  ipAddresses: string[];
  matchingFirewallRules: Array<Record<string, unknown>>;
  matchingAuditPolicies: Array<Record<string, unknown>>;
  matchingPolicies: Array<Record<string, unknown>>;
};

function records(value: unknown) {
  return Array.isArray(value) ? (value.filter((item) => item && typeof item === "object") as Array<Record<string, unknown>>) : [];
}

function mergeRecords(left: Array<Record<string, unknown>>, right: Array<Record<string, unknown>>) {
  const seen = new Set<string>();
  const merged: Array<Record<string, unknown>> = [];
  for (const item of [...left, ...right]) {
    const key = String(item.id ?? item.pos ?? item.comment ?? JSON.stringify(item));
    if (seen.has(key)) {
      continue;
    }
    seen.add(key);
    merged.push(item);
  }
  return merged;
}

function formatBytes(value: unknown) {
  const bytes = Number(value ?? 0);
  if (!Number.isFinite(bytes) || bytes <= 0) {
    return "0 B";
  }
  const units = ["B", "KB", "MB", "GB", "TB"];
  const index = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
  return `${(bytes / 1024 ** index).toFixed(index === 0 ? 0 : 1)} ${units[index]}`;
}

function summarizeTraffic(traffic: Array<Record<string, unknown>>) {
  const summaries = new Map<string, TrafficSummary>();
  for (const flow of traffic) {
    const source = String(flow.source ?? flow.source_ip ?? "external");
    const destination = String(flow.destination ?? flow.destination_ip ?? "external");
    const sourceLabel = String(flow.source_label ?? flow.source_ip ?? source);
    const destinationLabel = String(flow.destination_label ?? flow.destination_ip ?? destination);
    const protocol = String(flow.protocol ?? "unknown");
    const port = String(flow.port ?? flow.destination_port ?? "");
    const key = [source, destination, protocol, port, String(flow.interface ?? "")].join("|");
    const existing = summaries.get(key);
    const bytes = Number(flow.bytes ?? 0);
    const packets = Number(flow.packets ?? 0);
    const ipAddresses = Array.isArray(flow.ip_addresses) ? flow.ip_addresses.map(String) : [];
    const matchingFirewallRules = records(flow.matching_firewall_rules);
    const matchingAuditPolicies = records(flow.matching_audit_policies);
    const matchingPolicies = records(flow.matching_policies);
    if (existing) {
      existing.bytes += Number.isFinite(bytes) ? bytes : 0;
      existing.packets += Number.isFinite(packets) ? packets : 0;
      existing.count += 1;
      existing.ipAddresses = Array.from(new Set([...existing.ipAddresses, ...ipAddresses]));
      existing.matchingFirewallRules = mergeRecords(existing.matchingFirewallRules, matchingFirewallRules);
      existing.matchingAuditPolicies = mergeRecords(existing.matchingAuditPolicies, matchingAuditPolicies);
      existing.matchingPolicies = mergeRecords(existing.matchingPolicies, matchingPolicies);
      if (existing.decision === "observed" && String(flow.decision ?? "observed") !== "observed") {
        existing.decision = String(flow.decision ?? "observed");
      }
      continue;
    }
    summaries.set(key, {
      key,
      source,
      destination,
      sourceLabel,
      destinationLabel,
      protocol,
      port,
      bytes: Number.isFinite(bytes) ? bytes : 0,
      packets: Number.isFinite(packets) ? packets : 0,
      count: 1,
      decision: String(flow.decision ?? "observed"),
      interfaceName: String(flow.interface ?? ""),
      note: String(flow.note ?? ""),
      ipAddresses,
      matchingFirewallRules,
      matchingAuditPolicies,
      matchingPolicies,
    });
  }
  return Array.from(summaries.values()).sort((left, right) => right.bytes - left.bytes);
}

function endpointText(flow: TrafficSummary) {
  return `${flow.sourceLabel} -> ${flow.destinationLabel}`;
}

function totalBytes(traffic: TrafficSummary[]) {
  return traffic.reduce((sum, flow) => sum + flow.bytes, 0);
}

function TrafficBars({ traffic }: { traffic: TrafficSummary[] }) {
  const top = traffic.slice(0, 5);
  const max = Math.max(...top.map((flow) => flow.bytes), 1);
  if (!top.length) {
    return <div className="rounded-md border border-border p-3 text-xs text-slate-500">No traffic data yet.</div>;
  }

  return (
    <div className="space-y-1.5">
      {top.map((flow) => (
        <div key={flow.key} className="grid gap-1">
          <div className="flex items-center justify-between gap-3 text-xs">
            <span className="truncate">{endpointText(flow)}</span>
            <span className="shrink-0 text-slate-500">{formatBytes(flow.bytes)}</span>
          </div>
          <div className="h-2 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800">
            <div className="h-full rounded-full bg-accent" style={{ width: `${Math.max((flow.bytes / max) * 100, 4)}%` }} />
          </div>
        </div>
      ))}
      {Array.from({ length: Math.max(5 - top.length, 0) }).map((_, index) => (
        <div key={`empty-${index}`} className="grid gap-1 opacity-40">
          <div className="flex items-center justify-between gap-3 text-xs text-slate-500">
            <span>No additional flow</span>
            <span>0 B</span>
          </div>
          <div className="h-2 rounded-full bg-slate-200 dark:bg-slate-800" />
        </div>
      ))}
    </div>
  );
}

function CompactFlowList({ traffic }: { traffic: TrafficSummary[] }) {
  const top = traffic.filter((flow) => flow.protocol !== "interface-counter").slice(0, 3);
  if (!top.length) {
    const fallback = traffic.find((flow) => flow.protocol === "interface-counter");
    return (
      <div className="rounded-md border border-border p-2 text-xs text-slate-500">
        {fallback ? `Interface counter fallback: ${formatBytes(fallback.bytes)} observed.` : "No flow telemetry collected yet."}
      </div>
    );
  }

  return (
    <div className="divide-y divide-border rounded-md border border-border">
      {top.map((flow) => (
        <div key={flow.key} className="grid grid-cols-[1fr_auto] gap-3 px-3 py-2 text-xs">
          <div className="min-w-0">
            <div className="truncate font-medium">{endpointText(flow)}</div>
            <div className="truncate text-slate-500">{flow.protocol}{flow.port ? `:${flow.port}` : ""} · {flow.decision}</div>
          </div>
          <div className="shrink-0 text-right text-slate-500">{formatBytes(flow.bytes)}</div>
        </div>
      ))}
    </div>
  );
}

function ruleLabel(rule: Record<string, unknown>) {
  if (rule.error) {
    return String(rule.error);
  }
  const type = String(rule.type ?? "rule");
  const action = String(rule.action ?? "unknown");
  const proto = rule.proto ? String(rule.proto) : "any";
  const port = rule.dport || rule.sport ? `:${String(rule.dport ?? rule.sport)}` : "";
  return `${type} ${action} ${proto}${port}`;
}

function policyLabel(policy: Record<string, unknown>) {
  const name = String(policy.name ?? "Policy");
  const mode = String(policy.enforcement_mode ?? "enforced");
  const decision = String(policy.decision ?? "observed").replace("_", " ");
  const protocol = String(policy.protocol ?? "any");
  const ports = policy.ports ? `:${String(policy.ports)}` : "";
  return `${name} · ${mode} · ${decision} · ${protocol}${ports}`;
}

function decisionClass(decision: string) {
  if (decision.includes("would")) {
    return "border-amber-400/40 bg-amber-400/10 text-amber-300";
  }
  if (decision.includes("block")) {
    return "border-danger/40 bg-danger/10 text-danger";
  }
  if (decision.includes("allow")) {
    return "border-accent/40 bg-accent/10 text-accent";
  }
  return "border-border bg-canvas text-slate-500";
}

function FlowRuleContext({ flow }: { flow: TrafficSummary }) {
  const activeRules = flow.matchingFirewallRules.slice(0, 2);
  const auditPolicies = flow.matchingAuditPolicies.slice(0, 2);
  const hasContext = activeRules.length || auditPolicies.length;
  if (!hasContext) {
    return <div className="mt-1 text-xs text-slate-500">No matching active or audit rule.</div>;
  }

  return (
    <div className="mt-2 grid gap-1.5 text-xs">
      {activeRules.map((rule, index) => (
        <div key={`rule-${flow.key}-${index}`} className="rounded-md border border-border bg-canvas px-2 py-1">
          <span className="font-medium">Rule:</span> {ruleLabel(rule)}
          <span className={`ml-2 rounded border px-1.5 py-0.5 ${decisionClass(String(rule.decision ?? "observed"))}`}>{String(rule.decision ?? "observed")}</span>
        </div>
      ))}
      {auditPolicies.map((policy, index) => (
        <div key={`audit-${flow.key}-${index}`} className="rounded-md border border-amber-400/30 bg-amber-400/10 px-2 py-1 text-amber-200">
          <span className="font-medium">Audit:</span> {policyLabel(policy)}
        </div>
      ))}
      {flow.matchingFirewallRules.length + flow.matchingAuditPolicies.length > activeRules.length + auditPolicies.length ? (
        <div className="text-slate-500">
          {flow.matchingFirewallRules.length + flow.matchingAuditPolicies.length - activeRules.length - auditPolicies.length} more match
          {flow.matchingFirewallRules.length + flow.matchingAuditPolicies.length - activeRules.length - auditPolicies.length === 1 ? "" : "es"}
        </div>
      ) : null}
    </div>
  );
}

function ActiveRulesList({ rules, compact = false }: { rules: Array<Record<string, unknown>>; compact?: boolean }) {
  const visibleRules = compact ? rules.slice(0, 3) : rules;
  if (!rules.length) {
    return <div className="rounded-md border border-border p-2 text-xs text-slate-500">No active firewall rules were read for this workload.</div>;
  }

  return (
    <div className="divide-y divide-border rounded-md border border-border">
      {visibleRules.map((rule, index) => (
        <div key={`${String(rule.pos ?? index)}-${index}`} className="grid grid-cols-[1fr_auto] gap-3 px-3 py-2 text-xs">
          <div className="min-w-0">
            <div className="truncate font-medium">{ruleLabel(rule)}</div>
            <div className="truncate text-slate-500">{String(rule.comment ?? (rule.managed_by_nexafabric ? "NexaFabric managed" : "manual or provider rule"))}</div>
          </div>
          <div className={rule.enable === 0 ? "text-slate-500" : "text-accent"}>{rule.enable === 0 ? "off" : "on"}</div>
        </div>
      ))}
      {compact && rules.length > visibleRules.length ? (
        <div className="px-3 py-2 text-xs text-slate-500">{rules.length - visibleRules.length} more rule{rules.length - visibleRules.length === 1 ? "" : "s"} on detail page.</div>
      ) : null}
    </div>
  );
}

function ProtocolChart({ traffic }: { traffic: TrafficSummary[] }) {
  const protocolTotals = Array.from(
    traffic.reduce((map, flow) => map.set(flow.protocol, (map.get(flow.protocol) ?? 0) + flow.bytes), new Map<string, number>()),
  ).sort((left, right) => right[1] - left[1]);
  const total = protocolTotals.reduce((sum, [, bytes]) => sum + bytes, 0);
  const palette = ["#2dd4bf", "#60a5fa", "#f59e0b", "#f472b6", "#a78bfa"];

  if (!total) {
    return <div className="rounded-md border border-border p-3 text-xs text-slate-500">No protocol split available.</div>;
  }

  return (
    <div className="space-y-3">
      <div className="flex h-3 overflow-hidden rounded-full bg-slate-200 dark:bg-slate-800">
        {protocolTotals.map(([protocol, bytes], index) => {
          const width = (bytes / total) * 100;
          return <div key={protocol} title={protocol} style={{ width: `${width}%`, backgroundColor: palette[index % palette.length] }} />;
        })}
      </div>
      <div className="flex flex-wrap gap-2 text-xs">
        {protocolTotals.map(([protocol, bytes], index) => (
          <span key={protocol} className="inline-flex items-center gap-2 rounded-md border border-border px-2 py-1">
            <span className="h-2 w-2 rounded-full" style={{ backgroundColor: palette[index % palette.length] }} />
            {protocol} {formatBytes(bytes)}
          </span>
        ))}
      </div>
    </div>
  );
}

function WorkloadFacts({ insight }: { insight: WorkloadInsight }) {
  return (
    <div className="grid gap-3 md:grid-cols-4">
      <div className="rounded-md border border-border bg-panel p-3">
        <div className="flex items-center gap-2 text-xs text-slate-500"><CircuitBoard size={14} /> Type</div>
        <div className="mt-1 font-medium">{insight.workload.kind}</div>
      </div>
      <div className="rounded-md border border-border bg-panel p-3">
        <div className="flex items-center gap-2 text-xs text-slate-500"><Activity size={14} /> Status</div>
        <div className="mt-1 font-medium">{insight.workload.status}</div>
      </div>
      <div className="rounded-md border border-border bg-panel p-3">
        <div className="flex items-center gap-2 text-xs text-slate-500"><Hash size={14} /> VMID</div>
        <div className="mt-1 font-medium">{insight.workload.external_id}</div>
      </div>
      <div className="rounded-md border border-border bg-panel p-3">
        <div className="flex items-center gap-2 text-xs text-slate-500"><ShieldCheck size={14} /> Decision</div>
        <div className="mt-1 font-medium">{insight.effective_decision}</div>
      </div>
    </div>
  );
}

function TrafficTable({ traffic }: { traffic: TrafficSummary[] }) {
  return (
    <div className="overflow-hidden rounded-md border border-border">
      <div className="max-h-[460px] overflow-auto">
        <table className="w-full text-left text-sm">
          <thead className="sticky top-0 bg-panel text-xs uppercase text-slate-500">
            <tr>
              <th className="px-3 py-2 font-medium">Flow</th>
              <th className="px-3 py-2 font-medium">Protocol</th>
              <th className="px-3 py-2 font-medium">Decision</th>
              <th className="px-3 py-2 font-medium">Traffic</th>
              <th className="px-3 py-2 font-medium">Packets</th>
              <th className="px-3 py-2 font-medium">Seen</th>
            </tr>
          </thead>
          <tbody>
            {traffic.map((flow) => (
              <tr key={flow.key} className="border-t border-border">
                <td className="px-3 py-3">
                  <div className="font-medium">{endpointText(flow)}</div>
                  <div className="text-xs text-slate-500">{flow.ipAddresses.join(", ") || flow.interfaceName || "no endpoint metadata"}</div>
                  <FlowRuleContext flow={flow} />
                </td>
                <td className="px-3 py-3">{flow.protocol}{flow.port ? `:${flow.port}` : ""}</td>
                <td className="px-3 py-3">
                  <span className={`inline-flex rounded-md border px-2 py-1 text-xs ${decisionClass(flow.decision)}`}>{flow.decision.replace("_", " ")}</span>
                </td>
                <td className="px-3 py-3">{formatBytes(flow.bytes)}</td>
                <td className="px-3 py-3">{flow.packets}</td>
                <td className="px-3 py-3">{flow.count} sample{flow.count === 1 ? "" : "s"}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

export function Workloads() {
  const navigate = useNavigate();
  const workloads = useQuery({ queryKey: ["workloads"], queryFn: () => api<Workload[]>("/vms") });
  const [selectedId, setSelectedId] = useState("");
  const selected = selectedId || workloads.data?.[0]?.id || "";
  const insight = useQuery({
    queryKey: ["workload-insight", selected],
    queryFn: () => api<WorkloadInsight>(`/vms/${selected}/insights`),
    enabled: Boolean(selected),
  });
  const traffic = useMemo(() => summarizeTraffic(insight.data?.traffic ?? []), [insight.data?.traffic]);

  return (
    <>
      <PageHeader title="VMs/LXCs" subtitle="Inspect workloads, observed traffic, and matching policy decisions." />
      <div className="grid gap-4 xl:grid-cols-[minmax(0,1fr)_420px]">
        <section className="space-y-3">
          <DataTable
            rows={(workloads.data ?? []) as unknown as Record<string, unknown>[]}
            columns={[{ key: "name", label: "Name" }, { key: "kind", label: "Kind" }, { key: "status", label: "Status" }, { key: "external_id", label: "VMID" }]}
            selectedId={selected}
            onRowClick={(row) => setSelectedId(String(row.id))}
          />
        </section>
        <aside className="space-y-3 rounded-md border border-border bg-panel p-3">
          <div className="flex items-center justify-between gap-3">
            <div className="flex items-center gap-2 font-medium"><Activity size={18} /> Workload Summary</div>
            {selected ? (
              <button className={`${secondaryButtonClass} h-9 px-3`} onClick={() => navigate(`/workloads/${selected}`)}>
                <ArrowRight size={16} />
                Details
              </button>
            ) : null}
          </div>
          {insight.data ? (
            <div className="space-y-3 text-sm">
              <header>
                <div className="font-semibold">{insight.data.workload.name}</div>
                <div className="mt-1 text-xs text-slate-500">{insight.data.workload.kind} · {insight.data.workload.status} · VMID {insight.data.workload.external_id}</div>
              </header>
              <div className="grid grid-cols-3 gap-2">
                <div className="rounded-md border border-border bg-canvas p-2">
                  <div className="text-xs text-slate-500">Traffic</div>
                  <div className="mt-1 font-medium">{formatBytes(totalBytes(traffic))}</div>
                </div>
                <div className="rounded-md border border-border bg-canvas p-2">
                  <div className="text-xs text-slate-500">Flows</div>
                  <div className="mt-1 font-medium">{traffic.length}</div>
                </div>
                <div className="rounded-md border border-border bg-canvas p-2">
                  <div className="text-xs text-slate-500">IPs</div>
                  <div className="mt-1 font-medium">{insight.data.assigned_ips.length}</div>
                </div>
              </div>
              <section>
                <div className="mb-1.5 flex items-center gap-2 text-sm font-medium"><Network size={15} /> Assigned IPs</div>
                {insight.data.assigned_ips.length ? (
                  <div className="flex flex-wrap gap-2">
                    {insight.data.assigned_ips.slice(0, 4).map((ip) => (
                      <div key={ip.id} className="rounded-md border border-border px-2 py-1 text-xs">
                        <span className="font-medium">{ip.address}</span>
                        <span className="ml-2 text-slate-500">{ip.subnet_cidr ?? "unknown subnet"}</span>
                      </div>
                    ))}
                  </div>
                ) : (
                  <div className="rounded-md border border-border p-2 text-xs text-slate-500">No assigned IP address was discovered yet.</div>
                )}
              </section>
              <section>
                <div className="mb-1.5 text-sm font-medium">Top Traffic</div>
                <TrafficBars traffic={traffic} />
              </section>
              <section>
                <div className="mb-1.5 text-sm font-medium">Top Flows</div>
                <CompactFlowList traffic={traffic} />
              </section>
              <section>
                <div className="mb-1.5 flex items-center gap-2 text-sm font-medium"><Shield size={15} /> Active Rules</div>
                <ActiveRulesList rules={insight.data.active_firewall_rules ?? []} compact />
              </section>
            </div>
          ) : (
            <div className="text-sm text-slate-500">Select a workload.</div>
          )}
        </aside>
      </div>
    </>
  );
}

export function WorkloadDetail() {
  const { workloadId } = useParams();
  const insight = useQuery({
    queryKey: ["workload-insight", workloadId],
    queryFn: () => api<WorkloadInsight>(`/vms/${workloadId}/insights`),
    enabled: Boolean(workloadId),
  });
  const traffic = useMemo(() => summarizeTraffic(insight.data?.traffic ?? []), [insight.data?.traffic]);

  if (insight.isLoading) {
    return <div className="rounded-md border border-border bg-panel p-8 text-sm">Loading workload...</div>;
  }

  if (!insight.data) {
    return <div className="rounded-md border border-danger p-4 text-sm text-danger">Workload details could not be loaded.</div>;
  }

  return (
    <>
      <PageHeader title={insight.data.workload.name} subtitle="Detailed workload traffic, addressing, and policy context." />
      <div className="mb-4">
        <Link className={secondaryButtonClass} to="/workloads">Back to VMs/LXCs</Link>
      </div>
      <div className="space-y-4">
        <WorkloadFacts insight={insight.data} />
        <div className="grid gap-4 xl:grid-cols-[1fr_360px]">
          <section className="space-y-4">
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 flex items-center justify-between">
                <div className="font-medium">Traffic Distribution</div>
                <div className="text-xs text-slate-500">{traffic.length} aggregated flows · {formatBytes(totalBytes(traffic))}</div>
              </div>
              <TrafficBars traffic={traffic} />
            </div>
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 font-medium">Flow Table</div>
              {traffic.length ? <TrafficTable traffic={traffic} /> : <div className="rounded-md border border-border p-3 text-xs text-slate-500">No flow telemetry collected yet.</div>}
            </div>
          </section>
          <aside className="space-y-4">
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 font-medium">Protocol Split</div>
              <ProtocolChart traffic={traffic} />
            </div>
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 flex items-center gap-2 font-medium"><Network size={16} /> Assigned IPs</div>
              <div className="space-y-2">
                {insight.data.assigned_ips.map((ip) => (
                  <div key={ip.id} className="rounded-md border border-border px-3 py-2 text-xs">
                    <div className="font-medium">{ip.address}</div>
                    <div className="text-slate-500">{ip.subnet_cidr ?? "unknown subnet"} · {ip.status}</div>
                  </div>
                ))}
                {!insight.data.assigned_ips.length ? <div className="text-xs text-slate-500">No assigned IPs discovered.</div> : null}
              </div>
            </div>
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 flex items-center gap-2 font-medium"><Shield size={16} /> Active Firewall Rules</div>
              <ActiveRulesList rules={insight.data.active_firewall_rules ?? []} />
            </div>
            <div className="rounded-md border border-border bg-panel p-4">
              <div className="mb-3 font-medium">Matching Policies</div>
              <div className="space-y-2">
                {insight.data.matching_policies.map((policy) => (
                  <div key={policy.id} className="rounded-md border border-border p-3 text-sm">
                    <div className="font-medium">{policy.name}</div>
                    <div className="text-xs text-slate-500">v{policy.version} · {policy.enforcement_mode}</div>
                  </div>
                ))}
                {!insight.data.matching_policies.length ? <div className="text-xs text-slate-500">No matching policies.</div> : null}
              </div>
            </div>
            {insight.data.audit_mode_notes.length ? (
              <div className="rounded-md border border-border bg-panel p-4">
                <div className="mb-3 font-medium">Audit Mode</div>
                <div className="space-y-2">
                  {insight.data.audit_mode_notes.map((note) => <div key={note} className="rounded-md border border-border p-3 text-xs">{note}</div>)}
                </div>
              </div>
            ) : null}
          </aside>
        </div>
      </div>
    </>
  );
}
