// @File: frontend/src/pages/admin/cms/CmsWhmcsHealth.tsx
// @Desc: Admin health dashboard for the WHMCS API integration. Surfaces:
//        - A live "Ping WHMCS" button that hits
//          /api/v1/whmcs/admin/system/health-check and shows the last
//          response time + error classification (auth / IP / endpoint / …)
//        - The backend's public outbound IP (what to add to the WHMCS
//          API IP whitelist), derived from the health-check response or
//          from a separate /system/outbound-ip probe.
//        - A 24h uptime chart aggregated from the whmcs_api_logs table
//          via /api/v1/whmcs/admin/system/uptime-24h.
//        Uses the same auth pattern as CmsWhmcsLogs (Bearer token).
import { useCallback, useEffect, useMemo, useState } from "react";
import {
  Activity,
  AlertTriangle,
  CheckCircle2,
  Clock,
  Globe2,
  Loader2,
  RefreshCw,
  ShieldAlert,
  Wifi,
  XCircle,
} from "lucide-react";
import { toast } from "sonner";
import CmsLayout from "./CmsLayout";
import { PageHeader, SectionCard, Badge } from "./ui";
import { getToken } from "@/lib/auth";

// ---------- API response types ----------
interface HealthResponse {
  ok: boolean;
  http_status: number | null;
  latency_ms: number | null;
  cf_ray: string | null;
  server: string | null;
  message: string;
  body_snippet: string | null;
  api_url: string | null;
  error_category: string | null;
  whmcs_version: string | null;
  whmcs_system_url: string | null;
  authenticated_user: string | null;
  outbound_ip: string | null;
  outbound_ip_provider: string | null;
  ip_whitelist_hint: string | null;
}

interface UptimeBucket {
  hour_start: string;
  total: number;
  success: number;
  error: number;
  uptime_pct: number;
  avg_latency_ms: number | null;
}

interface UptimeResponse {
  buckets: UptimeBucket[];
  total_calls: number;
  total_success: number;
  total_errors: number;
  uptime_pct_24h: number;
  avg_latency_ms_24h: number | null;
  window_hours: number;
}

// ---------- Auth-aware fetch ----------
async function authFetch<T>(url: string, init?: RequestInit): Promise<T> {
  const token = getToken();
  const res = await fetch(url, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(init?.headers ?? {}),
    },
  });
  const raw = await res.text();
  let data: unknown = null;
  if (raw) {
    try {
      data = JSON.parse(raw);
    } catch {
      data = raw;
    }
  }
  if (!res.ok) {
    const detail =
      (data && typeof data === "object" && "detail" in (data as Record<string, unknown>)
        ? String((data as Record<string, unknown>).detail)
        : null) ||
      res.statusText ||
      "Request failed";
    throw new Error(detail);
  }
  return data as T;
}

// ---------- Error-category label ----------
const CATEGORY_LABELS: Record<string, { label: string; tone: "red" | "yellow" | "slate" }> = {
  auth_failed: { label: "Authentication failed", tone: "red" },
  ip_not_whitelisted: { label: "IP not whitelisted", tone: "red" },
  wrong_endpoint: { label: "Wrong endpoint URL", tone: "yellow" },
  invalid_action: { label: "Missing API role permission", tone: "yellow" },
  empty_response: { label: "Empty / invalid response", tone: "yellow" },
  transport_error: { label: "Transport error", tone: "red" },
  cloudflare_error: { label: "Cloudflare / origin error", tone: "red" },
};

function categoryBadge(cat: string | null) {
  if (!cat) return null;
  const meta = CATEGORY_LABELS[cat] ?? { label: cat, tone: "slate" as const };
  return <Badge tone={meta.tone}>{meta.label}</Badge>;
}

// ---------- Friendly hour label ----------
function formatHourLabel(iso: string): string {
  const d = new Date(iso);
  return d.toLocaleTimeString(undefined, {
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  });
}

// ---------- Main page ----------
export default function CmsWhmcsHealth() {
  const [health, setHealth] = useState<HealthResponse | null>(null);
  const [pinging, setPinging] = useState(false);
  const [lastPingedAt, setLastPingedAt] = useState<Date | null>(null);

  const [uptime, setUptime] = useState<UptimeResponse | null>(null);
  const [uptimeLoading, setUptimeLoading] = useState(false);

  const pingHealth = useCallback(async () => {
    setPinging(true);
    try {
      const data = await authFetch<HealthResponse>(
        "/api/v1/whmcs/admin/system/health-check",
        { method: "POST" },
      );
      setHealth(data);
      setLastPingedAt(new Date());
      if (data.ok) {
        toast.success("WHMCS connection healthy.");
      } else {
        toast.error(`WHMCS probe failed: ${data.message}`);
      }
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Ping failed.");
    } finally {
      setPinging(false);
    }
  }, []);

  const loadUptime = useCallback(async () => {
    setUptimeLoading(true);
    try {
      const data = await authFetch<UptimeResponse>(
        "/api/v1/whmcs/admin/system/uptime-24h?hours=24",
      );
      setUptime(data);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : "Failed to load uptime.");
    } finally {
      setUptimeLoading(false);
    }
  }, []);

  // Initial load + auto-refresh uptime every 2 minutes.
  useEffect(() => {
    pingHealth();
    loadUptime();
    const id = window.setInterval(() => {
      loadUptime();
    }, 120_000);
    return () => window.clearInterval(id);
  }, [pingHealth, loadUptime]);

  const outboundIp = health?.outbound_ip ?? null;

  // Bar chart geometry.
  const chartMaxCalls = useMemo(() => {
    if (!uptime) return 0;
    return Math.max(1, ...uptime.buckets.map((b) => b.total));
  }, [uptime]);

  return (
    <CmsLayout>
      <PageHeader
        title="WHMCS Health"
        description="Live connectivity probe, outbound IP, error classification, and 24h uptime."
      />

      {/* Top: live ping + summary cards */}
      <div className="grid gap-4 md:grid-cols-4 mb-6">
        <SummaryCard
          icon={<Activity className="w-5 h-5" />}
          label="Status"
          value={
            health == null
              ? "—"
              : health.ok
                ? "Healthy"
                : "Unhealthy"
          }
          tone={health == null ? "slate" : health.ok ? "green" : "red"}
        />
        <SummaryCard
          icon={<Clock className="w-5 h-5" />}
          label="Last response time"
          value={
            health?.latency_ms != null ? `${health.latency_ms} ms` : "—"
          }
          tone="slate"
        />
        <SummaryCard
          icon={<Wifi className="w-5 h-5" />}
          label="24h uptime"
          value={
            uptime ? `${uptime.uptime_pct_24h.toFixed(2)}%` : "—"
          }
          tone={
            uptime == null
              ? "slate"
              : uptime.uptime_pct_24h >= 99
                ? "green"
                : uptime.uptime_pct_24h >= 95
                  ? "amber"
                  : "red"
          }
        />
        <SummaryCard
          icon={<Globe2 className="w-5 h-5" />}
          label="Outbound IP"
          value={outboundIp ?? "—"}
          tone="slate"
          mono
        />
      </div>

      {/* Live ping panel */}
      <SectionCard
        title="Live connection probe"
        description="Pings the WHMCS API using WhmcsDetails and reports latency, HTTP status, and an error classification."
      >
        <div className="flex flex-wrap items-center gap-3 mb-4">
          <button
            type="button"
            onClick={pingHealth}
            disabled={pinging}
            className="inline-flex items-center gap-2 rounded-lg bg-[#0f5132] text-white text-sm font-semibold px-4 py-2 hover:bg-[#0b3d24] transition disabled:opacity-70"
          >
            {pinging ? (
              <Loader2 className="w-4 h-4 animate-spin" />
            ) : (
              <RefreshCw className="w-4 h-4" />
            )}
            {pinging ? "Pinging…" : "Ping WHMCS"}
          </button>
          {lastPingedAt && (
            <span className="text-xs text-slate-500">
              Last pinged {lastPingedAt.toLocaleTimeString()}
            </span>
          )}
          {health && (
            <span className="inline-flex items-center gap-2">
              {health.ok ? (
                <Badge tone="green">
                  <CheckCircle2 className="w-3 h-3" /> OK
                </Badge>
              ) : (
                <Badge tone="red">
                  <XCircle className="w-3 h-3" /> FAIL
                </Badge>
              )}
              {categoryBadge(health.error_category)}
            </span>
          )}
        </div>

        {health == null ? (
          <div className="text-sm text-slate-500 flex items-center gap-2">
            <Loader2 className="w-4 h-4 animate-spin" />
            Waiting for first probe…
          </div>
        ) : (
          <div className="grid gap-4 md:grid-cols-2">
            <InfoRow label="Message" value={health.message} />
            <InfoRow
              label="HTTP status"
              value={health.http_status != null ? String(health.http_status) : "—"}
            />
            <InfoRow
              label="Latency"
              value={
                health.latency_ms != null ? `${health.latency_ms} ms` : "—"
              }
            />
            <InfoRow label="API URL" value={health.api_url ?? "—"} mono />
            <InfoRow
              label="WHMCS version"
              value={health.whmcs_version ?? "—"}
            />
            <InfoRow
              label="Authenticated as"
              value={health.authenticated_user ?? "—"}
              mono
            />
            <InfoRow
              label="Cloudflare ray"
              value={health.cf_ray ?? "—"}
              mono
            />
            <InfoRow label="Server" value={health.server ?? "—"} mono />
          </div>
        )}

        {health?.ip_whitelist_hint && (
          <div
            className={`mt-5 rounded-lg border px-4 py-3 text-sm flex gap-3 ${
              health.error_category === "ip_not_whitelisted"
                ? "border-red-200 bg-red-50 text-red-800"
                : "border-amber-200 bg-amber-50 text-amber-800"
            }`}
          >
            <ShieldAlert className="w-4 h-4 flex-shrink-0 mt-0.5" />
            <div>
              <div className="font-semibold mb-0.5">
                IP whitelist guidance
              </div>
              <div className="leading-relaxed">{health.ip_whitelist_hint}</div>
            </div>
          </div>
        )}

        {health?.body_snippet && (
          <div className="mt-4">
            <div className="text-xs font-semibold text-slate-600 mb-1.5">
              Upstream body (first 300 chars)
            </div>
            <pre className="text-xs font-mono bg-slate-900 text-slate-100 rounded-lg p-3 overflow-x-auto whitespace-pre-wrap break-words">
              {health.body_snippet}
            </pre>
          </div>
        )}
      </SectionCard>

      {/* 24h uptime chart */}
      <SectionCard
        title="24h uptime & traffic"
        description="Hourly success/error counts from the whmcs_api_logs table. Auto-refreshes every 2 minutes."
      >
        <div className="flex items-center justify-between mb-4">
          <div className="flex items-center gap-4 text-xs text-slate-600">
            <span className="inline-flex items-center gap-1.5">
              <span className="w-3 h-3 rounded bg-emerald-500" /> Success
            </span>
            <span className="inline-flex items-center gap-1.5">
              <span className="w-3 h-3 rounded bg-red-500" /> Error
            </span>
          </div>
          <button
            type="button"
            onClick={loadUptime}
            disabled={uptimeLoading}
            className="inline-flex items-center gap-1.5 rounded-md border border-slate-300 bg-white text-slate-700 text-xs font-medium px-3 py-1.5 hover:bg-slate-50 disabled:opacity-70"
          >
            {uptimeLoading ? (
              <Loader2 className="w-3 h-3 animate-spin" />
            ) : (
              <RefreshCw className="w-3 h-3" />
            )}
            Refresh
          </button>
        </div>

        {uptime == null ? (
          <div className="text-sm text-slate-500 flex items-center gap-2 py-10 justify-center">
            <Loader2 className="w-4 h-4 animate-spin" />
            Loading uptime data…
          </div>
        ) : uptime.total_calls === 0 ? (
          <div className="text-sm text-slate-500 py-10 text-center flex flex-col items-center gap-2">
            <AlertTriangle className="w-5 h-5 text-amber-500" />
            No WHMCS API calls logged in the last 24 hours. The chart will
            populate as requests start flowing.
          </div>
        ) : (
          <>
            <div className="grid grid-cols-3 sm:grid-cols-4 gap-3 mb-5">
              <MiniStat
                label="Total calls"
                value={uptime.total_calls.toLocaleString()}
              />
              <MiniStat
                label="Success"
                value={uptime.total_success.toLocaleString()}
                tone="green"
              />
              <MiniStat
                label="Errors"
                value={uptime.total_errors.toLocaleString()}
                tone="red"
              />
              <MiniStat
                label="Avg latency"
                value={
                  uptime.avg_latency_ms_24h != null
                    ? `${uptime.avg_latency_ms_24h} ms`
                    : "—"
                }
              />
            </div>

            {/* Chart: stacked bars — green (success) stacked on red (error) */}
            <div className="relative">
              <div className="flex items-end gap-1 h-48 w-full border-b border-slate-200 pb-1">
                {uptime.buckets.map((b) => {
                  const heightPct = (b.total / chartMaxCalls) * 100;
                  const successShare =
                    b.total === 0 ? 0 : (b.success / b.total) * 100;
                  return (
                    <div
                      key={b.hour_start}
                      className="flex-1 flex flex-col justify-end h-full group relative min-w-0"
                    >
                      <div
                        className="w-full rounded-t overflow-hidden transition-all relative"
                        style={{
                          height: `${Math.max(heightPct, b.total > 0 ? 2 : 0)}%`,
                        }}
                      >
                        <div
                          className="w-full bg-red-500"
                          style={{ height: `${100 - successShare}%` }}
                        />
                        <div
                          className="w-full bg-emerald-500"
                          style={{ height: `${successShare}%` }}
                        />
                      </div>

                      {/* Tooltip */}
                      <div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-1 opacity-0 group-hover:opacity-100 transition pointer-events-none z-10">
                        <div className="rounded-md bg-slate-900 text-white text-[11px] px-2 py-1.5 whitespace-nowrap shadow-lg">
                          <div className="font-semibold">
                            {formatHourLabel(b.hour_start)}
                          </div>
                          <div>Total: {b.total}</div>
                          <div className="text-emerald-300">
                            Success: {b.success}
                          </div>
                          <div className="text-red-300">Error: {b.error}</div>
                          <div>Uptime: {b.uptime_pct.toFixed(2)}%</div>
                          {b.avg_latency_ms != null && (
                            <div>Avg: {b.avg_latency_ms} ms</div>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>

              {/* Hour axis labels — show every 4th bucket */}
              <div className="flex gap-1 mt-1">
                {uptime.buckets.map((b, i) => (
                  <div
                    key={b.hour_start}
                    className="flex-1 text-[10px] text-slate-400 text-center min-w-0"
                  >
                    {i % 4 === 0 ? formatHourLabel(b.hour_start) : ""}
                  </div>
                ))}
              </div>
            </div>
          </>
        )}
      </SectionCard>
    </CmsLayout>
  );
}

// ---------- Small presentational helpers ----------
function SummaryCard({
  icon,
  label,
  value,
  tone,
  mono,
}: {
  icon: React.ReactNode;
  label: string;
  value: string;
  tone: "green" | "red" | "amber" | "slate";
  mono?: boolean;
}) {
  const toneBg = {
    green: "bg-emerald-50 text-emerald-700",
    red: "bg-red-50 text-red-700",
    amber: "bg-amber-50 text-amber-700",
    slate: "bg-slate-100 text-slate-700",
  }[tone];

  return (
    <div className="rounded-xl border border-slate-200 bg-white p-4 shadow-sm">
      <div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wide text-slate-500 mb-2">
        <span className={`w-7 h-7 rounded-lg inline-flex items-center justify-center ${toneBg}`}>
          {icon}
        </span>
        {label}
      </div>
      <div
        className={`text-xl font-bold text-slate-900 ${mono ? "font-mono text-lg" : ""}`}
      >
        {value}
      </div>
    </div>
  );
}

function InfoRow({
  label,
  value,
  mono,
}: {
  label: string;
  value: string;
  mono?: boolean;
}) {
  return (
    <div>
      <div className="text-[11px] font-semibold uppercase tracking-wide text-slate-500 mb-0.5">
        {label}
      </div>
      <div
        className={`text-sm text-slate-900 break-all ${mono ? "font-mono" : ""}`}
      >
        {value}
      </div>
    </div>
  );
}

function MiniStat({
  label,
  value,
  tone = "slate",
}: {
  label: string;
  value: string;
  tone?: "green" | "red" | "slate";
}) {
  const toneCls = {
    green: "text-emerald-700",
    red: "text-red-700",
    slate: "text-slate-900",
  }[tone];
  return (
    <div className="rounded-lg border border-slate-200 bg-slate-50 px-3 py-2">
      <div className="text-[10px] font-semibold uppercase tracking-wide text-slate-500">
        {label}
      </div>
      <div className={`text-lg font-bold ${toneCls}`}>{value}</div>
    </div>
  );
}