import { useCallback, useEffect, useMemo, useState } from "react";
import { Link, useNavigate } from "react-router-dom";
import {
  LayoutDashboard, Server, Receipt, TicketCheck, Globe, Bell, LogOut, Search,
  Plus, User, Loader2, AlertCircle, RefreshCw, ChevronRight, Mail, FileText,
  Activity,
} from "lucide-react";
import { client } from "@/lib/api";
import { signOut } from "@/lib/auth";
import { useUserProfile } from "@/hooks/useUserProfile";
import { useWhmcsProfile } from "@/hooks/useWhmcsProfile";
import NotificationBell from "@/components/notifications/NotificationBell";
import { clientGetDashboard, clientListMonitors } from "@/lib/uptimerobotApi";

type SectionKey = "overview" | "services" | "invoices" | "domains" | "tickets" | "emails";

interface Service {
  id: number;
  product_name?: string | null;
  domain?: string | null;
  status?: string | null;
  regdate?: string | null;
  nextduedate?: string | null;
  billingcycle?: string | null;
  recurringamount?: string | null;
}

interface Invoice {
  id: number;
  invoicenum?: string | null;
  status?: string | null;
  date?: string | null;
  duedate?: string | null;
  datepaid?: string | null;
  total?: string | null;
  currency_code?: string | null;
}

interface Domain {
  id: number;
  domainname?: string | null;
  status?: string | null;
  registrar?: string | null;
  registrationdate?: string | null;
  expirydate?: string | null;
  recurringamount?: string | null;
}

interface Ticket {
  id: number;
  tid?: string | null;
  subject?: string | null;
  status?: string | null;
  priority?: string | null;
  department?: string | null;
  lastreply?: string | null;
}

interface EmailRecord {
  id: number;
  subject?: string | null;
  date?: string | null;
}

const NAV: Array<{ key: SectionKey; label: string; icon: typeof LayoutDashboard }> = [
  { key: "overview", label: "Overview", icon: LayoutDashboard },
  { key: "services", label: "Services", icon: Server },
  { key: "invoices", label: "Invoices", icon: Receipt },
  { key: "domains", label: "Domains", icon: Globe },
  { key: "tickets", label: "Tickets", icon: TicketCheck },
  { key: "emails", label: "Email History", icon: Mail },
];

function statusBadgeClass(status?: string | null): string {
  const s = (status || "").toLowerCase();
  if (["active", "paid", "open", "answered"].some((k) => s.includes(k))) {
    return "bg-emerald-100 text-emerald-700";
  }
  if (["pending", "provisioning", "customer-reply", "in progress"].some((k) => s.includes(k))) {
    return "bg-amber-100 text-amber-700";
  }
  if (["overdue", "cancelled", "suspended", "closed", "terminated"].some((k) => s.includes(k))) {
    return "bg-rose-100 text-rose-700";
  }
  return "bg-slate-200 text-slate-700";
}

/**
 * Error kind classification for dashboard list fetches.
 *
 * `transient` — the Atoms platform proxy or the backend replied with a
 *    gateway-style failure (502/503/504) or an HTML placeholder page. These
 *    happen during cold starts / restarts and virtually always recover if we
 *    just wait a few seconds.
 * `denied`    — the WHMCS API role does not allow the action (404/403 with a
 *    JSON body from our backend). This requires an admin fix, not a retry.
 * `other`     — anything else (network, 500, unexpected shape).
 */
type FetchErrorKind = "transient" | "denied" | "other";

class FetchListError extends Error {
  kind: FetchErrorKind;
  status?: number;
  constructor(message: string, kind: FetchErrorKind, status?: number) {
    super(message);
    this.name = "FetchListError";
    this.kind = kind;
    this.status = status;
  }
}

/**
 * Fetch a list from a backend endpoint with resilient error handling.
 *
 * The Atoms platform proxy occasionally returns an HTML "503 Temporarily
 * Unavailable" page instead of JSON when the backend service is restarting
 * or under cold-start. In that case `res.data` is a long HTML string rather
 * than a structured object, which would otherwise bubble up as a cryptic
 * runtime error.
 *
 * Strategy:
 *   - Retry transient proxy errors (502/503/504/HTML placeholder) up to
 *     `retries` times with exponential backoff capped at 6s per attempt.
 *     With `retries = 6` the sequence is 0.6s, 1.2s, 2.4s, 4.8s, 6s, 6s —
 *     a total worst-case wait of ~21s, which covers even slow cold-starts
 *     of the Atoms preview backend container.
 *   - Do NOT retry denied / 4xx errors — those need an admin fix, retrying
 *     just burns time.
 *   - Always surface a typed `FetchListError` so the caller can tell the
 *     user whether it's worth hitting "Refresh" or whether they should
 *     contact support.
 */
async function fetchList<T>(url: string, retries = 6): Promise<T[]> {
  const sleep = (ms: number) =>
    new Promise<void>((resolve) => setTimeout(resolve, ms));

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const classify = (res: any): { kind: FetchErrorKind; status?: number } | null => {
    if (!res) return { kind: "transient" };
    const status: number | undefined = res?.status;
    const data = res?.data;

    // Platform gateway / cold-start errors.
    if (status === 502 || status === 503 || status === 504) {
      return { kind: "transient", status };
    }

    // HTML placeholder returned by the Atoms proxy ("Temporarily Unavailable").
    if (
      typeof data === "string" &&
      data.trim().toLowerCase().startsWith("<!doctype")
    ) {
      return { kind: "transient", status };
    }

    // Our backend returns structured JSON for denials (e.g. missing API
    // permission). Don't retry those — they're deterministic failures.
    if (typeof status === "number" && status >= 400 && status < 500) {
      return { kind: "denied", status };
    }
    if (typeof status === "number" && status >= 500) {
      // Retry upstream 5xx once in case it's a transient hiccup.
      return { kind: "transient", status };
    }

    return null; // success
  };

  let lastError: FetchListError | null = null;
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      // Append a cache-busting `_t` param so no layer (browser, Service
      // Worker, platform proxy) can re-serve a cached 503 HTML body.
      const sep = url.includes("?") ? "&" : "?";
      const bustedUrl = `${url}${sep}_t=${Date.now()}`;
      const res = await client.apiCall.invoke({
        url: bustedUrl,
        method: "GET",
        data: {},
      });
      const problem = classify(res);
      if (!problem) {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        return ((res as any)?.data?.items as T[]) || [];
      }

      lastError = new FetchListError(
        `Request failed on ${url} (${problem.kind}${problem.status ? ` ${problem.status}` : ""})`,
        problem.kind,
        problem.status,
      );

      // Don't retry denied / 4xx — they won't recover on their own.
      if (problem.kind === "denied") {
        throw lastError;
      }
      if (attempt < retries) {
        // Exponential-ish backoff capped at 6s: 600, 1200, 2400, 4800, 6000, 6000 ms.
        await sleep(Math.min(600 * Math.pow(2, attempt), 6000));
        continue;
      }
      throw lastError;
    } catch (err) {
      if (err instanceof FetchListError) {
        lastError = err;
        if (err.kind === "denied") throw err;
      } else {
        lastError = new FetchListError(
          err instanceof Error ? err.message : `Request failed: ${url}`,
          "other",
        );
      }
      if (attempt < retries && lastError.kind !== "denied") {
        await sleep(Math.min(600 * Math.pow(2, attempt), 6000));
        continue;
      }
      throw lastError;
    }
  }
  throw lastError ?? new FetchListError(`Request failed: ${url}`, "other");
}

export default function ClientDashboard() {
  const navigate = useNavigate();
  const { loading: authLoading, authed, user, avatarUrl } = useUserProfile();
  const { loading: profileLoading, profile, notLinked, error: profileError, retrying: profileRetrying, refetch: refetchProfile } = useWhmcsProfile();

  const [active, setActive] = useState<SectionKey>("overview");
  const [services, setServices] = useState<Service[]>([]);
  const [invoices, setInvoices] = useState<Invoice[]>([]);
  const [domains, setDomains] = useState<Domain[]>([]);
  const [tickets, setTickets] = useState<Ticket[]>([]);
  const [emails, setEmails] = useState<EmailRecord[]>([]);
  const [listLoading, setListLoading] = useState(false);
  const [listError, setListError] = useState<string | null>(null);
  const [uptimeStats, setUptimeStats] = useState<{
    loaded: boolean;
    count: number;
    up: number;
    down: number;
    paused: number;
    status: "green" | "amber" | "red" | "none";
    lastAffectedId: number | null;
    lastAffectedName: string | null;
    lastAffectedStatus: string | null;
  }>({
    loaded: false,
    count: 0,
    up: 0,
    down: 0,
    paused: 0,
    status: "none",
    lastAffectedId: null,
    lastAffectedName: null,
    lastAffectedStatus: null,
  });

  const uptimeBadge = useMemo(() => {
    const palette = {
      green: { badgeClass: "bg-emerald-500/15 text-emerald-300", dotClass: "bg-emerald-400" },
      amber: { badgeClass: "bg-amber-500/15 text-amber-300", dotClass: "bg-amber-400" },
      red: { badgeClass: "bg-rose-500/20 text-rose-300", dotClass: "bg-rose-400" },
      none: { badgeClass: "bg-white/10 text-slate-300", dotClass: "bg-slate-400" },
    } as const;
    const p = palette[uptimeStats.status];
    return {
      loaded: uptimeStats.loaded,
      count: uptimeStats.count,
      up: uptimeStats.up,
      down: uptimeStats.down,
      paused: uptimeStats.paused,
      lastAffectedId: uptimeStats.lastAffectedId,
      lastAffectedName: uptimeStats.lastAffectedName,
      lastAffectedStatus: uptimeStats.lastAffectedStatus,
      badgeClass: p.badgeClass,
      dotClass: p.dotClass,
    };
  }, [uptimeStats]);

  const displayName = useMemo(() => {
    if (profile?.fullname) return profile.fullname;
    if (profile?.firstname || profile?.lastname) {
      return `${profile?.firstname ?? ""} ${profile?.lastname ?? ""}`.trim();
    }
    return user?.name || user?.email || "Account";
  }, [profile, user]);

  const initials = useMemo(() => {
    const base = displayName.trim();
    if (!base) return "U";
    const parts = base.split(/\s+/).slice(0, 2);
    return parts.map((p) => p[0]?.toUpperCase() || "").join("") || "U";
  }, [displayName]);

  const loadAll = useCallback(async () => {
    if (!profile) return;
    setListLoading(true);
    setListError(null);

    // Use Promise.allSettled so a single failing WHMCS endpoint (for example,
    // GetInvoices being denied by the API role) doesn't wipe out the rest of
    // the dashboard. Each section falls back to an empty list and we collect
    // per-section failure messages for a gentle warning banner.
    const results = await Promise.allSettled([
      fetchList<Service>("/api/v1/whmcs/services"),
      fetchList<Invoice>("/api/v1/whmcs/invoices"),
      fetchList<Domain>("/api/v1/whmcs/domains"),
      fetchList<Ticket>("/api/v1/whmcs/tickets"),
      fetchList<EmailRecord>("/api/v1/whmcs/emails"),
    ]);

    const labels = ["services", "invoices", "domains", "tickets", "emails"] as const;
    const setters = [setServices, setInvoices, setDomains, setTickets, setEmails] as const;
    const transientFailures: string[] = [];
    const deniedFailures: string[] = [];
    const otherFailures: string[] = [];

    results.forEach((res, idx) => {
      if (res.status === "fulfilled") {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (setters[idx] as any)(res.value);
      } else {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (setters[idx] as any)([]);
        const reason = res.reason;
        if (reason instanceof FetchListError) {
          if (reason.kind === "transient") transientFailures.push(labels[idx]);
          else if (reason.kind === "denied") deniedFailures.push(labels[idx]);
          else otherFailures.push(labels[idx]);
        } else {
          otherFailures.push(labels[idx]);
        }
      }
    });

    // Build a human-friendly message that distinguishes transient hiccups
    // (user just needs to click Refresh) from API-role denials (admin needs
    // to enable the permission in WHMCS).
    const parts: string[] = [];
    if (transientFailures.length) {
      parts.push(
        `The server is briefly unavailable, so we couldn't refresh ${transientFailures.join(
          ", ",
        )}. Please try again in a few seconds.`,
      );
    }
    if (deniedFailures.length) {
      parts.push(
        `Your account doesn't currently have access to ${deniedFailures.join(
          ", ",
        )}. Please contact support if this persists.`,
      );
    }
    if (otherFailures.length) {
      parts.push(`We couldn't load ${otherFailures.join(", ")}.`);
    }

    setListError(parts.length ? parts.join(" ") : null);
    setListLoading(false);
  }, [profile]);

  useEffect(() => {
    if (profile) loadAll();
  }, [profile, loadAll]);

  useEffect(() => {
    let cancelled = false;

    const classify = (s: string): "up" | "down" | "paused" | "other" => {
      const v = (s || "").toLowerCase();
      if (v.includes("paused")) return "paused";
      if (v.includes("down") || v.includes("seems_down")) return "down";
      if (v.includes("up") || v === "ok" || v.includes("active")) return "up";
      return "other";
    };

    const loadUptime = async () => {
      try {
        const dash = await clientGetDashboard();
        if (!dash?.subscription || dash.subscription.status !== "active") {
          if (!cancelled) {
            setUptimeStats({
              loaded: true,
              count: 0,
              up: 0,
              down: 0,
              paused: 0,
              status: "none",
              lastAffectedName: null,
              lastAffectedStatus: null,
            });
          }
          return;
        }
        let monitors: Array<{
          friendly_name?: string | null;
          status?: string | null;
          paused?: boolean;
          updated_at?: string | null;
          last_synced_at?: string | null;
        }> = [];
        try {
          monitors = await clientListMonitors();
        } catch {
          monitors = [];
        }
        const count = monitors.length;
        let up = 0;
        let down = 0;
        let paused = 0;
        for (const m of monitors) {
          const kind = m.paused ? "paused" : classify(m.status || "");
          if (kind === "up") up += 1;
          else if (kind === "down") down += 1;
          else if (kind === "paused") paused += 1;
        }

        let status: "green" | "amber" | "red" | "none" = "green";
        if (count === 0) status = "none";
        else if (down > 0) status = "red";
        else if (paused > 0) status = "amber";
        else status = "green";

        // Most recently affected = most recently updated monitor that is not "up",
        // falling back to the most recently updated monitor of any kind.
        const timeOf = (m: { updated_at?: string | null; last_synced_at?: string | null }) => {
          const t = m.updated_at || m.last_synced_at;
          const parsed = t ? Date.parse(t) : NaN;
          return Number.isFinite(parsed) ? parsed : 0;
        };
        const affected = monitors
          .filter((m) => (m.paused ? true : classify(m.status || "") !== "up"))
          .sort((a, b) => timeOf(b) - timeOf(a))[0];
        const fallback = [...monitors].sort((a, b) => timeOf(b) - timeOf(a))[0];
        const picked = affected || fallback || null;

        if (!cancelled) {
          setUptimeStats({
            loaded: true,
            count,
            up,
            down,
            paused,
            status,
            lastAffectedId: picked?.id ?? null,
            lastAffectedName: picked?.friendly_name || null,
            lastAffectedStatus: picked
              ? (picked.paused ? "paused" : (picked.status || null))
              : null,
          });
        }
      } catch {
        if (!cancelled) {
          setUptimeStats({
            loaded: true,
            count: 0,
            up: 0,
            down: 0,
            paused: 0,
            status: "none",
            lastAffectedId: null,
            lastAffectedName: null,
            lastAffectedStatus: null,
          });
        }
      }
    };

    if (profile) {
      loadUptime();
      const id = window.setInterval(loadUptime, 60_000);
      return () => {
        cancelled = true;
        window.clearInterval(id);
      };
    }
    return () => {
      cancelled = true;
    };
  }, [profile]);

  // If the auth check has finished and the user is NOT signed in, automatically
  // bounce them to /login. This replaces the old "Sign in required" dead-end
  // screen: returning visitors who already have a valid session stay on the
  // dashboard, and everyone else is taken straight to the sign-in form.
  useEffect(() => {
    if (!authLoading && !authed) {
      navigate("/login", { replace: true });
    }
  }, [authLoading, authed, navigate]);

  // Stateless JWT — just clear the token locally and send the user to /login.
  const handleLogout = () => {
    signOut("/login");
  };

  // ---------- Loading / auth gates ----------
  if (authLoading || profileLoading) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-50 text-slate-600">
        <div className="flex flex-col items-center text-center px-6">
          <Loader2 className="w-6 h-6 animate-spin mb-3 text-[#0b6e3e]" />
          <p className="font-semibold text-slate-700">
            {profileRetrying ? "Reconnecting to our servers..." : "Loading your dashboard..."}
          </p>
          {profileRetrying && (
            <p className="text-xs text-slate-500 mt-1 max-w-sm">
              The service is briefly unavailable while it restarts. This should only take a few seconds.
            </p>
          )}
        </div>
      </div>
    );
  }

  // Not authenticated — a redirect to /login has been scheduled by the effect
  // above. Render a lightweight loader while the navigation happens so the
  // user never sees a flashed "Sign in required" card.
  if (!authed) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-50 text-slate-600">
        <div className="flex flex-col items-center text-center px-6">
          <Loader2 className="w-6 h-6 animate-spin mb-3 text-[#0b6e3e]" />
          <p className="font-semibold text-slate-700">Redirecting to sign in…</p>
        </div>
      </div>
    );
  }

  if (notLinked) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-50 p-6">
        <div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8 max-w-md text-center">
          <AlertCircle className="w-10 h-10 mx-auto text-amber-500 mb-3" />
          <h1 className="text-xl font-bold text-slate-900">Finish your registration</h1>
          <p className="text-sm text-slate-500 mt-2">
            Your DoItEasy account is not linked to a WHMCS client yet. Complete registration to
            unlock services, invoices, and support.
          </p>
          <Link
            to="/register"
            className="mt-5 inline-block w-full py-2.5 rounded-md bg-[#0b6e3e] text-white font-bold text-sm hover:bg-[#0a8048] transition"
          >
            Complete registration
          </Link>
          <button
            onClick={refetchProfile}
            className="mt-3 w-full py-2 text-sm text-slate-500 hover:underline inline-flex items-center justify-center gap-1"
          >
            <RefreshCw className="w-3.5 h-3.5" /> I've already registered — retry
          </button>
        </div>
      </div>
    );
  }

  if (profileError) {
    return (
      <div className="min-h-screen flex items-center justify-center bg-slate-50 p-6">
        <div className="bg-white rounded-2xl shadow-sm border border-slate-200 p-8 max-w-md text-center">
          <AlertCircle className="w-10 h-10 mx-auto text-rose-500 mb-3" />
          <h1 className="text-xl font-bold text-slate-900">Could not load your account</h1>
          <p className="text-sm text-slate-500 mt-2">{profileError}</p>
          <button
            onClick={refetchProfile}
            className="mt-5 w-full py-2.5 rounded-md bg-[#0b6e3e] text-white font-bold text-sm hover:bg-[#0a8048] transition"
          >
            Try again
          </button>
        </div>
      </div>
    );
  }

  // ---------- Section renderers ----------
  const renderOverview = () => {
    const dueInvoices = invoices.filter((inv) =>
      ["unpaid", "overdue", "draft"].some((k) => (inv.status || "").toLowerCase().includes(k)),
    );
    const openTickets = tickets.filter((t) =>
      ["open", "answered", "customer-reply", "in progress"].some((k) =>
        (t.status || "").toLowerCase().includes(k),
      ),
    );

    const stats = [
      { label: "Active Services", value: services.length.toString(), icon: Server, color: "from-emerald-500 to-emerald-600" },
      { label: "Domains", value: domains.length.toString(), icon: Globe, color: "from-blue-500 to-blue-600" },
      { label: "Invoices Due", value: dueInvoices.length.toString(), icon: Receipt, color: "from-rose-500 to-rose-600" },
      { label: "Open Tickets", value: openTickets.length.toString(), icon: TicketCheck, color: "from-amber-500 to-amber-600" },
    ];

    return (
      <>
        <div className="rounded-2xl p-6 bg-gradient-to-r from-[#0b6e3e] to-[#063019] text-white relative overflow-hidden">
          <div className="absolute -right-10 -top-10 w-48 h-48 rounded-full bg-[#f5c400]/20 blur-2xl" />
          <div className="relative flex items-center justify-between flex-wrap gap-4">
            <div>
              <h2 className="text-2xl font-bold">Welcome back, {profile?.firstname || "there"} 👋</h2>
              <p className="text-slate-200 mt-1 text-sm">
                You have <span className="text-[#f5c400] font-semibold">{openTickets.length} open ticket{openTickets.length === 1 ? "" : "s"}</span>{" "}
                and <span className="text-[#f5c400] font-semibold">{dueInvoices.length} invoice{dueInvoices.length === 1 ? "" : "s"}</span> pending.
              </p>
            </div>
            <div className="flex gap-2">
              <Link
                to="/hosting"
                className="px-4 py-2 rounded-md bg-[#f5c400] text-slate-900 font-bold text-sm hover:bg-yellow-400 transition inline-flex items-center gap-2"
              >
                <Plus className="w-4 h-4" /> Order Service
              </Link>
              <Link
                to="/tickets"
                className="px-4 py-2 rounded-md bg-white/10 border border-white/20 text-white font-bold text-sm hover:bg-white/20 transition"
              >
                Open Ticket
              </Link>
            </div>
          </div>
        </div>

        <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
          {stats.map((s) => (
            <div
              key={s.label}
              className="bg-white rounded-2xl p-5 border border-slate-200 shadow-sm hover:shadow-md transition"
            >
              <div className="flex items-start justify-between">
                <div>
                  <p className="text-sm text-slate-500">{s.label}</p>
                  <p className="text-2xl font-bold text-slate-900 mt-1">{s.value}</p>
                </div>
                <div
                  className={`w-11 h-11 rounded-xl bg-gradient-to-br ${s.color} text-white flex items-center justify-center`}
                >
                  <s.icon className="w-5 h-5" />
                </div>
              </div>
            </div>
          ))}
        </div>

        <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
          <div className="flex items-center justify-between p-5 border-b border-slate-100">
            <h3 className="font-bold text-slate-900">Recent Services</h3>
            <button
              onClick={() => setActive("services")}
              className="text-sm text-[#0b6e3e] font-semibold flex items-center gap-1 hover:underline"
            >
              View all <ChevronRight className="w-4 h-4" />
            </button>
          </div>
          {services.length === 0 ? (
            <div className="p-8 text-center text-sm text-slate-500">No services yet.</div>
          ) : (
            <div className="overflow-x-auto">
              <table className="w-full text-sm">
                <thead className="bg-slate-50 text-slate-600">
                  <tr>
                    <th className="text-left p-3 font-semibold">Service</th>
                    <th className="text-left p-3 font-semibold">Domain</th>
                    <th className="text-left p-3 font-semibold">Billing</th>
                    <th className="text-left p-3 font-semibold">Next Due</th>
                    <th className="text-left p-3 font-semibold">Status</th>
                  </tr>
                </thead>
                <tbody>
                  {services.slice(0, 5).map((s) => (
                    <tr key={s.id} className="border-t border-slate-100 hover:bg-slate-50">
                      <td className="p-3 font-semibold text-slate-900">{s.product_name || `Service #${s.id}`}</td>
                      <td className="p-3 text-slate-600">{s.domain || "—"}</td>
                      <td className="p-3 text-slate-600">{s.billingcycle || "—"}</td>
                      <td className="p-3 text-slate-600">{s.nextduedate || "—"}</td>
                      <td className="p-3">
                        <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${statusBadgeClass(s.status)}`}>
                          {s.status || "—"}
                        </span>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </div>
      </>
    );
  };

  const renderServices = () => (
    <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
      <div className="p-5 border-b border-slate-100 flex items-center justify-between">
        <h3 className="font-bold text-slate-900">All Services</h3>
        <span className="text-xs text-slate-500">{services.length} total</span>
      </div>
      {services.length === 0 ? (
        <div className="p-8 text-center text-sm text-slate-500">No active services.</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-slate-50 text-slate-600">
              <tr>
                <th className="text-left p-3 font-semibold">Service</th>
                <th className="text-left p-3 font-semibold">Domain</th>
                <th className="text-left p-3 font-semibold">Billing</th>
                <th className="text-left p-3 font-semibold">Amount</th>
                <th className="text-left p-3 font-semibold">Registered</th>
                <th className="text-left p-3 font-semibold">Next Due</th>
                <th className="text-left p-3 font-semibold">Status</th>
              </tr>
            </thead>
            <tbody>
              {services.map((s) => (
                <tr key={s.id} className="border-t border-slate-100 hover:bg-slate-50">
                  <td className="p-3 font-semibold text-slate-900">{s.product_name || `Service #${s.id}`}</td>
                  <td className="p-3 text-slate-600">{s.domain || "—"}</td>
                  <td className="p-3 text-slate-600">{s.billingcycle || "—"}</td>
                  <td className="p-3 text-slate-600">{s.recurringamount || "—"}</td>
                  <td className="p-3 text-slate-600">{s.regdate || "—"}</td>
                  <td className="p-3 text-slate-600">{s.nextduedate || "—"}</td>
                  <td className="p-3">
                    <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${statusBadgeClass(s.status)}`}>
                      {s.status || "—"}
                    </span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );

  const renderInvoices = () => (
    <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
      <div className="p-5 border-b border-slate-100 flex items-center justify-between">
        <h3 className="font-bold text-slate-900">Invoices</h3>
        <span className="text-xs text-slate-500">{invoices.length} total</span>
      </div>
      {invoices.length === 0 ? (
        <div className="p-8 text-center text-sm text-slate-500">No invoices yet.</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-slate-50 text-slate-600">
              <tr>
                <th className="text-left p-3 font-semibold">Invoice #</th>
                <th className="text-left p-3 font-semibold">Date</th>
                <th className="text-left p-3 font-semibold">Due Date</th>
                <th className="text-left p-3 font-semibold">Paid</th>
                <th className="text-left p-3 font-semibold">Total</th>
                <th className="text-left p-3 font-semibold">Status</th>
              </tr>
            </thead>
            <tbody>
              {invoices.map((inv) => (
                <tr key={inv.id} className="border-t border-slate-100 hover:bg-slate-50">
                  <td className="p-3 font-semibold text-slate-900">{inv.invoicenum || inv.id}</td>
                  <td className="p-3 text-slate-600">{inv.date || "—"}</td>
                  <td className="p-3 text-slate-600">{inv.duedate || "—"}</td>
                  <td className="p-3 text-slate-600">{inv.datepaid && inv.datepaid !== "0000-00-00" ? inv.datepaid : "—"}</td>
                  <td className="p-3 font-semibold">
                    {inv.total ? `${inv.total}${inv.currency_code ? ` ${inv.currency_code}` : ""}` : "—"}
                  </td>
                  <td className="p-3">
                    <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${statusBadgeClass(inv.status)}`}>
                      {inv.status || "—"}
                    </span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );

  const renderDomains = () => (
    <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
      <div className="p-5 border-b border-slate-100 flex items-center justify-between">
        <h3 className="font-bold text-slate-900">Domains</h3>
        <span className="text-xs text-slate-500">{domains.length} total</span>
      </div>
      {domains.length === 0 ? (
        <div className="p-8 text-center text-sm text-slate-500">No domains registered.</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-slate-50 text-slate-600">
              <tr>
                <th className="text-left p-3 font-semibold">Domain</th>
                <th className="text-left p-3 font-semibold">Registrar</th>
                <th className="text-left p-3 font-semibold">Registered</th>
                <th className="text-left p-3 font-semibold">Expires</th>
                <th className="text-left p-3 font-semibold">Amount</th>
                <th className="text-left p-3 font-semibold">Status</th>
              </tr>
            </thead>
            <tbody>
              {domains.map((d) => (
                <tr key={d.id} className="border-t border-slate-100 hover:bg-slate-50">
                  <td className="p-3 font-semibold text-slate-900">{d.domainname || `Domain #${d.id}`}</td>
                  <td className="p-3 text-slate-600">{d.registrar || "—"}</td>
                  <td className="p-3 text-slate-600">{d.registrationdate || "—"}</td>
                  <td className="p-3 text-slate-600">{d.expirydate || "—"}</td>
                  <td className="p-3 text-slate-600">{d.recurringamount || "—"}</td>
                  <td className="p-3">
                    <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${statusBadgeClass(d.status)}`}>
                      {d.status || "—"}
                    </span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );

  const renderTickets = () => (
    <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
      <div className="p-5 border-b border-slate-100 flex items-center justify-between">
        <h3 className="font-bold text-slate-900">Support Tickets</h3>
        <Link
          to="/tickets"
          className="text-sm text-[#0b6e3e] font-semibold flex items-center gap-1 hover:underline"
        >
          New ticket <ChevronRight className="w-4 h-4" />
        </Link>
      </div>
      {tickets.length === 0 ? (
        <div className="p-8 text-center text-sm text-slate-500">No tickets opened.</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-slate-50 text-slate-600">
              <tr>
                <th className="text-left p-3 font-semibold">Ticket</th>
                <th className="text-left p-3 font-semibold">Subject</th>
                <th className="text-left p-3 font-semibold">Department</th>
                <th className="text-left p-3 font-semibold">Priority</th>
                <th className="text-left p-3 font-semibold">Last Reply</th>
                <th className="text-left p-3 font-semibold">Status</th>
              </tr>
            </thead>
            <tbody>
              {tickets.map((t) => (
                <tr key={t.id} className="border-t border-slate-100 hover:bg-slate-50">
                  <td className="p-3 font-semibold text-slate-900">{t.tid || t.id}</td>
                  <td className="p-3 text-slate-700">{t.subject || "—"}</td>
                  <td className="p-3 text-slate-600">{t.department || "—"}</td>
                  <td className="p-3 text-slate-600">{t.priority || "—"}</td>
                  <td className="p-3 text-slate-600">{t.lastreply || "—"}</td>
                  <td className="p-3">
                    <span className={`text-xs font-semibold px-2.5 py-1 rounded-full ${statusBadgeClass(t.status)}`}>
                      {t.status || "—"}
                    </span>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );

  const renderEmails = () => (
    <div className="bg-white rounded-2xl border border-slate-200 shadow-sm">
      <div className="p-5 border-b border-slate-100 flex items-center justify-between">
        <div>
          <h3 className="font-bold text-slate-900">Email History</h3>
          <p className="text-xs text-slate-500 mt-1">
            All notifications the billing system has sent to {profile?.email || user?.email || "your email"}.
          </p>
        </div>
        <span className="text-xs text-slate-500">{emails.length} total</span>
      </div>
      {emails.length === 0 ? (
        <div className="p-8 text-center text-sm text-slate-500">No email history yet.</div>
      ) : (
        <div className="overflow-x-auto">
          <table className="w-full text-sm">
            <thead className="bg-slate-50 text-slate-600">
              <tr>
                <th className="text-left p-3 font-semibold">Subject</th>
                <th className="text-left p-3 font-semibold w-56">Date</th>
              </tr>
            </thead>
            <tbody>
              {emails.map((em) => (
                <tr key={em.id} className="border-t border-slate-100 hover:bg-slate-50">
                  <td className="p-3 text-slate-800">{em.subject || `Email #${em.id}`}</td>
                  <td className="p-3 text-slate-600">{em.date || "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}
    </div>
  );

  const renderActive = () => {
    switch (active) {
      case "services":
        return renderServices();
      case "invoices":
        return renderInvoices();
      case "domains":
        return renderDomains();
      case "tickets":
        return renderTickets();
      case "emails":
        return renderEmails();
      case "overview":
      default:
        return renderOverview();
    }
  };

  return (
    <div className="min-h-screen bg-slate-50 flex">
      {/* SIDEBAR */}
      <aside className="w-64 bg-[#063019] text-white flex flex-col sticky top-0 h-screen">
        <div className="p-5 border-b border-white/10">
          <Link to="/" className="flex items-center gap-2">
            <img
              src="/logo.svg"
              alt="DoItEasy"
              className="h-10 md:h-12 w-auto brightness-0 invert"
              onError={(e) => ((e.target as HTMLImageElement).style.display = "none")}
            />
          </Link>
          <p className="text-xs text-slate-400 mt-2">Client Portal</p>
        </div>
        <nav className="flex-1 overflow-y-auto py-4">
          {NAV.map((n) => (
            <button
              key={n.key}
              onClick={() => setActive(n.key)}
              className={`w-full flex items-center gap-3 px-5 py-2.5 text-sm transition ${
                active === n.key
                  ? "bg-[#0b6e3e] text-white border-l-4 border-[#f5c400]"
                  : "text-slate-300 hover:bg-white/5 hover:text-white border-l-4 border-transparent"
              }`}
            >
              <n.icon className="w-4 h-4" />
              {n.label}
            </button>
          ))}
          <Link
            to="/client/uptime"
            className="group relative w-full flex items-center gap-3 px-5 py-2.5 text-sm transition text-slate-300 hover:bg-white/5 hover:text-white border-l-4 border-transparent"
          >
            <Activity className="w-4 h-4" />
            <span className="flex-1">Uptime Monitoring</span>
            {uptimeBadge.loaded && (
              <span
                className={`flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-semibold ${uptimeBadge.badgeClass}`}
              >
                <span className={`h-2 w-2 rounded-full ${uptimeBadge.dotClass}`} />
                {uptimeBadge.count}
              </span>
            )}
            {uptimeBadge.loaded && uptimeBadge.count > 0 && (
              <div
                role="tooltip"
                className="pointer-events-none absolute left-full top-1/2 z-30 ml-3 w-64 -translate-y-1/2 translate-x-1 rounded-lg border border-white/10 bg-slate-900/95 p-3 text-left text-xs text-slate-200 opacity-0 shadow-xl transition-opacity duration-150 group-hover:pointer-events-auto group-hover:translate-x-0 group-hover:opacity-100 group-focus-within:opacity-100"
              >
                <div className="mb-2 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
                  Uptime Monitors
                </div>
                <div className="grid grid-cols-3 gap-2 mb-2">
                  <div className="flex flex-col items-center rounded bg-emerald-500/10 py-1.5">
                    <span className="text-sm font-semibold text-emerald-300">{uptimeBadge.up}</span>
                    <span className="text-[10px] uppercase tracking-wide text-emerald-300/80">Up</span>
                  </div>
                  <div className="flex flex-col items-center rounded bg-rose-500/10 py-1.5">
                    <span className="text-sm font-semibold text-rose-300">{uptimeBadge.down}</span>
                    <span className="text-[10px] uppercase tracking-wide text-rose-300/80">Down</span>
                  </div>
                  <div className="flex flex-col items-center rounded bg-amber-500/10 py-1.5">
                    <span className="text-sm font-semibold text-amber-300">{uptimeBadge.paused}</span>
                    <span className="text-[10px] uppercase tracking-wide text-amber-300/80">Paused</span>
                  </div>
                </div>
                <div className="border-t border-white/10 pt-2">
                  <div className="text-[10px] uppercase tracking-wide text-slate-400">
                    Most recently affected
                  </div>
                  {uptimeBadge.lastAffectedName ? (
                    <div className="mt-1 flex items-center justify-between gap-2">
                      {uptimeBadge.lastAffectedId !== null ? (
                        <Link
                          to={`/client/uptime?monitor=${uptimeBadge.lastAffectedId}`}
                          className="truncate font-medium text-emerald-300 hover:text-emerald-200 underline-offset-2 hover:underline"
                          title={`Open ${uptimeBadge.lastAffectedName}`}
                        >
                          {uptimeBadge.lastAffectedName}
                        </Link>
                      ) : (
                        <span
                          className="truncate font-medium text-slate-100"
                          title={uptimeBadge.lastAffectedName}
                        >
                          {uptimeBadge.lastAffectedName}
                        </span>
                      )}
                      {uptimeBadge.lastAffectedStatus && (
                        <span className="shrink-0 rounded bg-white/10 px-1.5 py-0.5 text-[10px] capitalize text-slate-200">
                          {uptimeBadge.lastAffectedStatus.replace(/_/g, " ")}
                        </span>
                      )}
                    </div>
                  ) : (
                    <div className="mt-1 text-slate-400">No incidents — all clear.</div>
                  )}
                </div>
              </div>
            )}
          </Link>
          <Link
            to="/account/profile"
            className="w-full flex items-center gap-3 px-5 py-2.5 text-sm transition text-slate-300 hover:bg-white/5 hover:text-white border-l-4 border-transparent"
          >
            <User className="w-4 h-4" /> Profile & Security
          </Link>
        </nav>
        <div className="p-4 border-t border-white/10 space-y-2">
          <Link
            to="/service-terms"
            className="flex items-center gap-2 text-xs text-slate-400 hover:text-[#f5c400] transition"
          >
            <FileText className="w-3.5 h-3.5" /> Legal · Service Terms
          </Link>
          <Link
            to="/refund-policy"
            className="flex items-center gap-2 text-xs text-slate-400 hover:text-[#f5c400] transition"
          >
            <FileText className="w-3.5 h-3.5" /> Legal · Refund Policy
          </Link>
          <Link
            to="/privacy-center"
            className="flex items-center gap-2 text-xs text-slate-400 hover:text-[#f5c400] transition"
          >
            <FileText className="w-3.5 h-3.5" /> Legal · Privacy Center
          </Link>
          <button
            onClick={handleLogout}
            className="flex items-center gap-2 text-sm text-slate-300 hover:text-[#f5c400]"
          >
            <LogOut className="w-4 h-4" /> Sign out
          </button>
        </div>
      </aside>

      {/* MAIN */}
      <div className="flex-1 flex flex-col">
        {/* TOPBAR */}
        <header className="h-16 bg-white border-b border-slate-200 flex items-center justify-between px-6 sticky top-0 z-10">
          <div className="flex items-center gap-3">
            <h1 className="text-lg font-bold text-slate-900 capitalize">
              {NAV.find((n) => n.key === active)?.label || "Dashboard"}
            </h1>
          </div>
          <div className="flex items-center gap-4">
            <div className="relative hidden md:block">
              <Search className="w-4 h-4 absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
              <input
                placeholder="Search services, tickets…"
                className="pl-9 pr-4 py-2 bg-slate-100 rounded-lg text-sm w-72 outline-none focus:ring-2 focus:ring-[#0b6e3e]/20"
              />
            </div>
            <button
              onClick={() => {
                refetchProfile();
                loadAll();
              }}
              className="w-10 h-10 rounded-lg hover:bg-slate-100 flex items-center justify-center"
              title="Refresh"
            >
              <RefreshCw className="w-5 h-5 text-slate-600" />
            </button>
            <NotificationBell
              onOpenTicket={(ticketId) => {
                navigate(`/support/tickets?ticket=${encodeURIComponent(ticketId)}`);
              }}
            />
            <Link to="/account/profile" className="flex items-center gap-2">
              <div className="w-9 h-9 rounded-full bg-[#0b6e3e] text-white flex items-center justify-center font-bold text-sm overflow-hidden">
                {avatarUrl ? (
                  <img src={avatarUrl} alt="Avatar" className="w-full h-full object-cover" />
                ) : (
                  initials
                )}
              </div>
              <div className="hidden md:block">
                <div className="text-sm font-bold text-slate-900">{displayName}</div>
                <div className="text-xs text-slate-500">
                  {profile?.company || profile?.email || user?.email || "Client"}
                </div>
              </div>
            </Link>
          </div>
        </header>

        {/* CONTENT */}
        <main className="flex-1 p-6 space-y-6">
          {listError && (
            <div className="flex items-start gap-3 p-3 rounded-lg bg-amber-50 border border-amber-200 text-sm text-amber-900">
              <AlertCircle className="w-4 h-4 shrink-0 mt-0.5 text-amber-600" />
              <div className="flex-1">
                <div>{listError}</div>
                <button
                  type="button"
                  onClick={loadAll}
                  disabled={listLoading}
                  className="mt-2 inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-white border border-amber-300 text-amber-900 text-xs font-semibold hover:bg-amber-100 transition disabled:opacity-60"
                >
                  <RefreshCw className={`w-3.5 h-3.5 ${listLoading ? "animate-spin" : ""}`} />
                  Retry now
                </button>
              </div>
            </div>
          )}
          {listLoading && !listError && (
            <div className="flex items-center gap-2 text-sm text-slate-500">
              <Loader2 className="w-4 h-4 animate-spin" /> Refreshing WHMCS data…
            </div>
          )}
          {renderActive()}
        </main>
      </div>
    </div>
  );
}