// @File: frontend/src/pages/admin/cms/CmsSupport.tsx
// @Desc: Unified admin page for the AI Chat Support System. Tabs:
//        - Overview (analytics)
//        - Departments
//        - Seats (support agents + roles + status)
//        - Routing rules
//        - Knowledge base (manual articles + WHMCS product ingestion)
//        All actions go through the /api/v1/chat/admin/* endpoints via
//        the web-sdk so everything is admin-gated server-side.
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { createClient } from "@metagptx/web-sdk";
import { Link } from "react-router-dom";
import {
  Loader2,
  Plus,
  Trash2,
  Save,
  RefreshCcw,
  Sparkles,
  Users,
  Route,
  BookOpen,
  BarChart3,
  AlertCircle,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Badge } from "@/components/ui/badge";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@/components/ui/tabs";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import CmsLayout from "./CmsLayout";

const client = createClient();

const ROLE_OPTIONS = [
  { value: "admin", label: "Admin" },
  { value: "supervisor", label: "Supervisor" },
  { value: "support_agent", label: "Support Agent" },
  { value: "billing_agent", label: "Billing Agent" },
  { value: "technical_agent", label: "Technical Agent" },
];

const SOURCE_OPTIONS = [
  { value: "manual", label: "Manual" },
  { value: "website", label: "Website" },
  { value: "whmcs_product", label: "WHMCS Product" },
  { value: "faq", label: "FAQ" },
];

interface Dept {
  id: number;
  name: string;
  slug: string;
  description?: string;
  is_active: boolean;
}

interface Seat {
  id: number;
  display_name: string;
  email: string;
  role: string;
  department_id?: number | null;
  status: string;
  chat_limit: number;
  availability?: { status: string; last_heartbeat_at?: string | null };
}

interface RoutingRule {
  id: number;
  name: string;
  priority: number;
  match_conditions: Record<string, unknown>;
  target_department_id?: number | null;
  target_seat_id?: number | null;
  is_active: boolean;
}

interface KBArticle {
  id: number;
  title: string;
  slug: string;
  source: string;
  source_url?: string | null;
  tags?: string | null;
  is_published: boolean;
  content: string;
  updated_at?: string | null;
}

export default function CmsSupport() {
  const [tab, setTab] = useState("overview");
  return (
    <CmsLayout title="AI Chat Support">
      <div className="space-y-4">
        <p className="text-sm text-muted-foreground">
          Configure departments, support agents, routing rules, the knowledge
          base, and view chat analytics. Visitors talk to the AI assistant
          first; when it hands over, chats appear in the{" "}
          <Link to="/agent/support" className="font-medium text-emerald-700 underline">
            Agent Inbox
          </Link>
          .
        </p>
        <Tabs value={tab} onValueChange={setTab}>
          <TabsList className="flex flex-wrap">
            <TabsTrigger value="overview">
              <BarChart3 className="mr-1.5 h-3.5 w-3.5" /> Overview
            </TabsTrigger>
            <TabsTrigger value="departments">
              <Users className="mr-1.5 h-3.5 w-3.5" /> Departments
            </TabsTrigger>
            <TabsTrigger value="seats">
              <Users className="mr-1.5 h-3.5 w-3.5" /> Agents
            </TabsTrigger>
            <TabsTrigger value="routing">
              <Route className="mr-1.5 h-3.5 w-3.5" /> Routing
            </TabsTrigger>
            <TabsTrigger value="kb">
              <BookOpen className="mr-1.5 h-3.5 w-3.5" /> Knowledge Base
            </TabsTrigger>
          </TabsList>
          <TabsContent value="overview" className="mt-4">
            <OverviewTab />
          </TabsContent>
          <TabsContent value="departments" className="mt-4">
            <DepartmentsTab />
          </TabsContent>
          <TabsContent value="seats" className="mt-4">
            <SeatsTab />
          </TabsContent>
          <TabsContent value="routing" className="mt-4">
            <RoutingTab />
          </TabsContent>
          <TabsContent value="kb" className="mt-4">
            <KBTab />
          </TabsContent>
        </Tabs>
      </div>
    </CmsLayout>
  );
}

// --------- Overview / Analytics ---------
function OverviewTab() {
  const [stats, setStats] = useState<any | null>(null);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [window, setWindow] = useState(7);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const resp = await client.apiCall.invoke({
        url: "/api/v1/chat/admin/analytics",
        method: "GET",
        data: { window_days: window },
      });
      setStats(resp.data);
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to load analytics");
    } finally {
      setLoading(false);
    }
  }, [window]);

  useEffect(() => {
    load();
  }, [load]);

  if (loading) {
    return (
      <div className="flex items-center gap-2 text-sm text-muted-foreground">
        <Loader2 className="h-4 w-4 animate-spin" /> Loading analytics…
      </div>
    );
  }
  if (err) {
    return <ErrorNote message={err} />;
  }

  return (
    <div className="space-y-4">
      <div className="flex items-center justify-between gap-2">
        <div className="flex items-center gap-2 text-sm">
          <span>Window:</span>
          <Select value={String(window)} onValueChange={(v) => setWindow(Number(v))}>
            <SelectTrigger className="w-32">
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {[1, 7, 30, 90].map((d) => (
                <SelectItem key={d} value={String(d)}>
                  Last {d}d
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
        <Button size="sm" variant="outline" onClick={load}>
          <RefreshCcw className="mr-1.5 h-3.5 w-3.5" /> Refresh
        </Button>
      </div>

      <div className="grid gap-3 md:grid-cols-4">
        <Stat label="Total chats" value={stats?.total_chats} />
        <Stat label="AI resolved" value={stats?.ai_resolved} />
        <Stat label="Human resolved" value={stats?.human_resolved} />
        <Stat label="Escalations" value={stats?.escalations} />
        <Stat
          label="AI deflection"
          value={
            stats?.ai_deflection_rate != null
              ? `${Math.round((stats.ai_deflection_rate as number) * 100)}%`
              : "–"
          }
        />
        <Stat
          label="Avg first response"
          value={fmtSec(stats?.avg_first_response_sec)}
        />
        <Stat label="Avg resolution" value={fmtSec(stats?.avg_resolution_sec)} />
        <Stat label="SLA breaches" value={stats?.sla_breaches} />
      </div>

      <div className="rounded-lg border bg-card p-4">
        <h3 className="mb-3 text-sm font-semibold">Daily volume</h3>
        {stats?.volume_by_day?.length ? (
          <div className="flex items-end gap-1 h-40">
            {stats.volume_by_day.map((v: any) => {
              const max = Math.max(
                1,
                ...stats.volume_by_day.map((x: any) => x.count)
              );
              const height = Math.round((v.count / max) * 100);
              return (
                <div
                  key={v.date}
                  className="flex-1 flex flex-col items-center justify-end gap-1"
                  title={`${v.date}: ${v.count} chats`}
                >
                  <div
                    className="w-full rounded-t bg-emerald-500"
                    style={{ height: `${Math.max(6, height)}%` }}
                  />
                  <div className="text-[10px] text-muted-foreground">
                    {v.date.slice(5)}
                  </div>
                </div>
              );
            })}
          </div>
        ) : (
          <div className="text-sm text-muted-foreground">
            No chats in the selected window.
          </div>
        )}
      </div>

      <div className="rounded-lg border bg-muted/20 p-3 text-xs text-muted-foreground">
        Tip: notify supervisors of unattended chats by hitting{" "}
        <code className="rounded bg-muted px-1">
          POST /api/v1/chat/admin/notifications/missed-chat
        </code>
        . This is safe to call on a cron.
      </div>
    </div>
  );
}

function Stat({ label, value }: { label: string; value: any }) {
  return (
    <div className="rounded-lg border bg-card p-3">
      <div className="text-[11px] uppercase tracking-wide text-muted-foreground">
        {label}
      </div>
      <div className="mt-1 text-2xl font-semibold">{value ?? "–"}</div>
    </div>
  );
}

function fmtSec(n: any): string {
  if (!n || typeof n !== "number") return "–";
  if (n < 60) return `${n}s`;
  if (n < 3600) return `${Math.round(n / 60)}m`;
  return `${Math.round((n / 3600) * 10) / 10}h`;
}

// --------- Departments ---------
function DepartmentsTab() {
  const [items, setItems] = useState<Dept[]>([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [draft, setDraft] = useState({ name: "", slug: "", description: "" });
  const [saving, setSaving] = useState(false);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const resp = await client.apiCall.invoke({
        url: "/api/v1/chat/departments",
        method: "GET",
        data: {},
      });
      setItems(resp.data?.items || []);
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to load");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const addDept = async () => {
    if (!draft.name.trim()) return;
    setSaving(true);
    try {
      await client.apiCall.invoke({
        url: "/api/v1/chat/admin/departments",
        method: "POST",
        data: { ...draft, is_active: true },
      });
      setDraft({ name: "", slug: "", description: "" });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to add");
    } finally {
      setSaving(false);
    }
  };

  const updateDept = async (d: Dept, patch: Partial<Dept>) => {
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/departments/${d.id}`,
        method: "PATCH",
        data: { ...d, ...patch },
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to save");
    }
  };

  const delDept = async (id: number) => {
    if (!window.confirm("Delete this department?")) return;
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/departments/${id}`,
        method: "DELETE",
        data: {},
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to delete");
    }
  };

  return (
    <div className="space-y-4">
      {err && <ErrorNote message={err} />}
      <div className="rounded-lg border bg-card p-4">
        <h3 className="mb-3 text-sm font-semibold">Add department</h3>
        <div className="grid gap-2 md:grid-cols-4">
          <Input
            placeholder="Name (e.g. Billing)"
            value={draft.name}
            onChange={(e) => setDraft({ ...draft, name: e.target.value })}
          />
          <Input
            placeholder="Slug (optional)"
            value={draft.slug}
            onChange={(e) => setDraft({ ...draft, slug: e.target.value })}
          />
          <Input
            placeholder="Description"
            value={draft.description}
            onChange={(e) => setDraft({ ...draft, description: e.target.value })}
          />
          <Button onClick={addDept} disabled={saving || !draft.name.trim()}>
            <Plus className="mr-1 h-4 w-4" /> Add
          </Button>
        </div>
      </div>

      {loading ? (
        <LoadingRow />
      ) : (
        <div className="rounded-lg border bg-card divide-y">
          {items.length === 0 && (
            <div className="p-4 text-sm text-muted-foreground">
              No departments yet.
            </div>
          )}
          {items.map((d) => (
            <div
              key={d.id}
              className="flex flex-wrap items-center gap-2 p-3 text-sm"
            >
              <Input
                value={d.name}
                onChange={(e) =>
                  setItems((p) =>
                    p.map((x) => (x.id === d.id ? { ...x, name: e.target.value } : x))
                  )
                }
                className="w-48"
              />
              <Input
                value={d.slug}
                onChange={(e) =>
                  setItems((p) =>
                    p.map((x) => (x.id === d.id ? { ...x, slug: e.target.value } : x))
                  )
                }
                className="w-40"
              />
              <Input
                value={d.description || ""}
                onChange={(e) =>
                  setItems((p) =>
                    p.map((x) =>
                      x.id === d.id ? { ...x, description: e.target.value } : x
                    )
                  )
                }
                className="flex-1 min-w-[200px]"
              />
              <div className="flex items-center gap-1.5">
                <Switch
                  checked={d.is_active}
                  onCheckedChange={(v) => updateDept(d, { is_active: v })}
                />
                <span className="text-xs text-muted-foreground">Active</span>
              </div>
              <Button size="sm" variant="outline" onClick={() => updateDept(d, {})}>
                <Save className="mr-1 h-3.5 w-3.5" /> Save
              </Button>
              <Button
                size="sm"
                variant="ghost"
                className="text-red-600"
                onClick={() => delDept(d.id)}
              >
                <Trash2 className="h-3.5 w-3.5" />
              </Button>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// --------- Seats ---------
function SeatsTab() {
  const [items, setItems] = useState<Seat[]>([]);
  const [depts, setDepts] = useState<Dept[]>([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [draft, setDraft] = useState({
    display_name: "",
    email: "",
    role: "support_agent",
    department_id: "" as string | "",
    chat_limit: 5,
  });
  const [saving, setSaving] = useState(false);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const [s, d] = await Promise.all([
        client.apiCall.invoke({
          url: "/api/v1/chat/admin/seats",
          method: "GET",
          data: {},
        }),
        client.apiCall.invoke({
          url: "/api/v1/chat/departments",
          method: "GET",
          data: {},
        }),
      ]);
      setItems(s.data?.items || []);
      setDepts(d.data?.items || []);
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to load");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const addSeat = async () => {
    if (!draft.display_name.trim() || !draft.email.trim()) return;
    setSaving(true);
    try {
      await client.apiCall.invoke({
        url: "/api/v1/chat/admin/seats",
        method: "POST",
        data: {
          display_name: draft.display_name.trim(),
          email: draft.email.trim(),
          role: draft.role,
          department_id: draft.department_id
            ? Number(draft.department_id)
            : undefined,
          chat_limit: Number(draft.chat_limit) || 5,
        },
      });
      setDraft({
        display_name: "",
        email: "",
        role: "support_agent",
        department_id: "",
        chat_limit: 5,
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to add");
    } finally {
      setSaving(false);
    }
  };

  const updateSeat = async (s: Seat, patch: Partial<Seat>) => {
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/seats/${s.id}`,
        method: "PATCH",
        data: patch,
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to save");
    }
  };

  const delSeat = async (id: number) => {
    if (!window.confirm("Delete this seat?")) return;
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/seats/${id}`,
        method: "DELETE",
        data: {},
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to delete");
    }
  };

  return (
    <div className="space-y-4">
      {err && <ErrorNote message={err} />}
      <div className="rounded-lg border bg-card p-4">
        <h3 className="mb-3 text-sm font-semibold">Add support agent</h3>
        <div className="grid gap-2 md:grid-cols-6">
          <Input
            placeholder="Display name"
            value={draft.display_name}
            onChange={(e) => setDraft({ ...draft, display_name: e.target.value })}
          />
          <Input
            placeholder="Email"
            type="email"
            value={draft.email}
            onChange={(e) => setDraft({ ...draft, email: e.target.value })}
          />
          <Select
            value={draft.role}
            onValueChange={(v) => setDraft({ ...draft, role: v })}
          >
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {ROLE_OPTIONS.map((r) => (
                <SelectItem key={r.value} value={r.value}>
                  {r.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Select
            value={draft.department_id || "none"}
            onValueChange={(v) =>
              setDraft({ ...draft, department_id: v === "none" ? "" : v })
            }
          >
            <SelectTrigger>
              <SelectValue placeholder="Department" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="none">— None —</SelectItem>
              {depts.map((d) => (
                <SelectItem key={d.id} value={String(d.id)}>
                  {d.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Input
            type="number"
            min={1}
            max={50}
            placeholder="Chat limit"
            value={draft.chat_limit}
            onChange={(e) =>
              setDraft({ ...draft, chat_limit: Number(e.target.value) })
            }
          />
          <Button
            onClick={addSeat}
            disabled={saving || !draft.display_name.trim() || !draft.email.trim()}
          >
            <Plus className="mr-1 h-4 w-4" /> Add
          </Button>
        </div>
        <p className="mt-2 text-xs text-muted-foreground">
          The email must match the agent's Atoms account email so they inherit
          this seat when they sign in.
        </p>
      </div>

      {loading ? (
        <LoadingRow />
      ) : (
        <div className="rounded-lg border bg-card divide-y">
          {items.length === 0 && (
            <div className="p-4 text-sm text-muted-foreground">
              No agents yet.
            </div>
          )}
          {items.map((s) => (
            <div key={s.id} className="grid gap-2 p-3 text-sm md:grid-cols-6">
              <Input
                value={s.display_name}
                onChange={(e) =>
                  setItems((p) =>
                    p.map((x) =>
                      x.id === s.id ? { ...x, display_name: e.target.value } : x
                    )
                  )
                }
              />
              <div className="flex items-center text-xs text-muted-foreground break-all">
                {s.email}
              </div>
              <Select
                value={s.role}
                onValueChange={(v) => updateSeat(s, { role: v })}
              >
                <SelectTrigger>
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  {ROLE_OPTIONS.map((r) => (
                    <SelectItem key={r.value} value={r.value}>
                      {r.label}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <Select
                value={
                  s.department_id ? String(s.department_id) : "none"
                }
                onValueChange={(v) =>
                  updateSeat(s, {
                    department_id: v === "none" ? null : (Number(v) as any),
                  })
                }
              >
                <SelectTrigger>
                  <SelectValue placeholder="Department" />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="none">— None —</SelectItem>
                  {depts.map((d) => (
                    <SelectItem key={d.id} value={String(d.id)}>
                      {d.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <div className="flex items-center gap-2">
                <Input
                  type="number"
                  min={1}
                  max={50}
                  value={s.chat_limit}
                  onChange={(e) =>
                    setItems((p) =>
                      p.map((x) =>
                        x.id === s.id
                          ? { ...x, chat_limit: Number(e.target.value) }
                          : x
                      )
                    )
                  }
                  className="w-20"
                />
                <Badge
                  variant={
                    s.availability?.status === "online"
                      ? "default"
                      : s.availability?.status === "busy"
                      ? "secondary"
                      : "outline"
                  }
                  className={
                    s.availability?.status === "online"
                      ? "bg-emerald-600 text-white"
                      : undefined
                  }
                >
                  {s.availability?.status || "offline"}
                </Badge>
              </div>
              <div className="flex items-center gap-1 justify-end">
                <Select
                  value={s.status}
                  onValueChange={(v) => updateSeat(s, { status: v })}
                >
                  <SelectTrigger className="w-28">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem value="active">Active</SelectItem>
                    <SelectItem value="suspended">Suspended</SelectItem>
                  </SelectContent>
                </Select>
                <Button
                  size="sm"
                  variant="outline"
                  onClick={() =>
                    updateSeat(s, {
                      display_name: s.display_name,
                      chat_limit: s.chat_limit,
                    })
                  }
                >
                  <Save className="h-3.5 w-3.5" />
                </Button>
                <Button
                  size="sm"
                  variant="ghost"
                  className="text-red-600"
                  onClick={() => delSeat(s.id)}
                >
                  <Trash2 className="h-3.5 w-3.5" />
                </Button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// --------- Routing rules ---------
function RoutingTab() {
  const [items, setItems] = useState<RoutingRule[]>([]);
  const [depts, setDepts] = useState<Dept[]>([]);
  const [seats, setSeats] = useState<Seat[]>([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [draft, setDraft] = useState({
    name: "",
    priority: 100,
    keywords: "",
    authenticated: "any" as "any" | "yes" | "no",
    target_department_id: "",
    target_seat_id: "",
  });

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const [r, d, s] = await Promise.all([
        client.apiCall.invoke({
          url: "/api/v1/chat/admin/routing-rules",
          method: "GET",
          data: {},
        }),
        client.apiCall.invoke({
          url: "/api/v1/chat/departments",
          method: "GET",
          data: {},
        }),
        client.apiCall.invoke({
          url: "/api/v1/chat/admin/seats",
          method: "GET",
          data: {},
        }),
      ]);
      setItems(r.data?.items || []);
      setDepts(d.data?.items || []);
      setSeats(s.data?.items || []);
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to load");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const buildConditions = () => {
    const cond: Record<string, unknown> = {};
    const kws = draft.keywords
      .split(",")
      .map((k) => k.trim())
      .filter(Boolean);
    if (kws.length) cond.keywords = kws;
    if (draft.authenticated === "yes") cond.authenticated = true;
    if (draft.authenticated === "no") cond.authenticated = false;
    return cond;
  };

  const addRule = async () => {
    if (!draft.name.trim()) return;
    try {
      await client.apiCall.invoke({
        url: "/api/v1/chat/admin/routing-rules",
        method: "POST",
        data: {
          name: draft.name.trim(),
          priority: Number(draft.priority) || 100,
          match_conditions: buildConditions(),
          target_department_id: draft.target_department_id
            ? Number(draft.target_department_id)
            : null,
          target_seat_id: draft.target_seat_id
            ? Number(draft.target_seat_id)
            : null,
          is_active: true,
        },
      });
      setDraft({
        name: "",
        priority: 100,
        keywords: "",
        authenticated: "any",
        target_department_id: "",
        target_seat_id: "",
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to add");
    }
  };

  const toggleRule = async (r: RoutingRule) => {
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/routing-rules/${r.id}`,
        method: "PATCH",
        data: {
          name: r.name,
          priority: r.priority,
          match_conditions: r.match_conditions,
          target_department_id: r.target_department_id,
          target_seat_id: r.target_seat_id,
          is_active: !r.is_active,
        },
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to save");
    }
  };

  const delRule = async (id: number) => {
    if (!window.confirm("Delete rule?")) return;
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/routing-rules/${id}`,
        method: "DELETE",
        data: {},
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to delete");
    }
  };

  return (
    <div className="space-y-4">
      {err && <ErrorNote message={err} />}
      <div className="rounded-lg border bg-card p-4">
        <h3 className="mb-3 text-sm font-semibold">Add routing rule</h3>
        <div className="grid gap-2 md:grid-cols-4">
          <Input
            placeholder="Rule name"
            value={draft.name}
            onChange={(e) => setDraft({ ...draft, name: e.target.value })}
          />
          <Input
            type="number"
            placeholder="Priority (lower runs first)"
            value={draft.priority}
            onChange={(e) =>
              setDraft({ ...draft, priority: Number(e.target.value) })
            }
          />
          <Input
            placeholder="Keywords (comma sep, e.g. invoice, refund)"
            value={draft.keywords}
            onChange={(e) => setDraft({ ...draft, keywords: e.target.value })}
          />
          <Select
            value={draft.authenticated}
            onValueChange={(v: any) => setDraft({ ...draft, authenticated: v })}
          >
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="any">Any auth state</SelectItem>
              <SelectItem value="yes">Authenticated only</SelectItem>
              <SelectItem value="no">Anonymous only</SelectItem>
            </SelectContent>
          </Select>
          <Select
            value={draft.target_department_id || "none"}
            onValueChange={(v) =>
              setDraft({
                ...draft,
                target_department_id: v === "none" ? "" : v,
              })
            }
          >
            <SelectTrigger>
              <SelectValue placeholder="Route to department" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="none">— Any department —</SelectItem>
              {depts.map((d) => (
                <SelectItem key={d.id} value={String(d.id)}>
                  {d.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Select
            value={draft.target_seat_id || "none"}
            onValueChange={(v) =>
              setDraft({ ...draft, target_seat_id: v === "none" ? "" : v })
            }
          >
            <SelectTrigger>
              <SelectValue placeholder="Route to seat (optional)" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="none">— No specific seat —</SelectItem>
              {seats.map((s) => (
                <SelectItem key={s.id} value={String(s.id)}>
                  {s.display_name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Button onClick={addRule} className="md:col-span-2">
            <Plus className="mr-1 h-4 w-4" /> Add rule
          </Button>
        </div>
      </div>

      {loading ? (
        <LoadingRow />
      ) : (
        <div className="rounded-lg border bg-card divide-y">
          {items.length === 0 && (
            <div className="p-4 text-sm text-muted-foreground">
              No rules yet. Chats will go to the default queue.
            </div>
          )}
          {items.map((r) => {
            const dept = depts.find((d) => d.id === r.target_department_id);
            const seat = seats.find((s) => s.id === r.target_seat_id);
            return (
              <div
                key={r.id}
                className="flex flex-wrap items-center gap-2 p-3 text-sm"
              >
                <div className="font-medium min-w-[120px]">{r.name}</div>
                <Badge variant="outline">priority {r.priority}</Badge>
                <div className="text-xs text-muted-foreground break-all flex-1 min-w-[200px]">
                  {JSON.stringify(r.match_conditions || {})}
                </div>
                <div className="text-xs">
                  → {dept?.name || "any dept"}
                  {seat ? `, ${seat.display_name}` : ""}
                </div>
                <Switch
                  checked={r.is_active}
                  onCheckedChange={() => toggleRule(r)}
                />
                <Button
                  size="sm"
                  variant="ghost"
                  className="text-red-600"
                  onClick={() => delRule(r.id)}
                >
                  <Trash2 className="h-3.5 w-3.5" />
                </Button>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// --------- Knowledge Base ---------
function KBTab() {
  const [items, setItems] = useState<KBArticle[]>([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [ingesting, setIngesting] = useState(false);
  const [draft, setDraft] = useState<{
    title: string;
    content: string;
    source: string;
    tags: string;
  }>({ title: "", content: "", source: "manual", tags: "" });
  const [expandedId, setExpandedId] = useState<number | null>(null);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const resp = await client.apiCall.invoke({
        url: "/api/v1/chat/admin/kb",
        method: "GET",
        data: { limit: 200 },
      });
      setItems(resp.data?.items || []);
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to load");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  const add = async () => {
    if (!draft.title.trim() || !draft.content.trim()) return;
    try {
      await client.apiCall.invoke({
        url: "/api/v1/chat/admin/kb",
        method: "POST",
        data: {
          title: draft.title.trim(),
          content: draft.content,
          source: draft.source,
          tags: draft.tags || null,
          is_published: true,
        },
      });
      setDraft({ title: "", content: "", source: "manual", tags: "" });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to add");
    }
  };

  const save = async (a: KBArticle) => {
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/kb/${a.id}`,
        method: "PATCH",
        data: {
          title: a.title,
          content: a.content,
          source: a.source,
          source_url: a.source_url || null,
          tags: a.tags || null,
          is_published: a.is_published,
        },
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to save");
    }
  };

  const del = async (id: number) => {
    if (!window.confirm("Delete this article?")) return;
    try {
      await client.apiCall.invoke({
        url: `/api/v1/chat/admin/kb/${id}`,
        method: "DELETE",
        data: {},
      });
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to delete");
    }
  };

  const ingestFromWhmcs = async () => {
    setIngesting(true);
    try {
      const resp = await client.apiCall.invoke({
        url: "/api/v1/chat/admin/kb/ingest-whmcs-products",
        method: "POST",
        data: {},
      });
      const { imported, total_products } = resp.data || {};
      alert(
        `Imported ${imported || 0} new products of ${total_products || 0} total.`
      );
      await load();
    } catch (e: any) {
      setErr(e?.data?.detail || e?.message || "Failed to ingest");
    } finally {
      setIngesting(false);
    }
  };

  return (
    <div className="space-y-4">
      {err && <ErrorNote message={err} />}

      <div className="flex flex-wrap gap-2">
        <Button
          variant="outline"
          onClick={ingestFromWhmcs}
          disabled={ingesting}
        >
          {ingesting ? (
            <Loader2 className="mr-1.5 h-4 w-4 animate-spin" />
          ) : (
            <Sparkles className="mr-1.5 h-4 w-4" />
          )}
          Import WHMCS products as KB
        </Button>
        <div className="text-xs text-muted-foreground self-center">
          Pulls product names + descriptions so the AI can answer
          service/pricing questions.
        </div>
      </div>

      <div className="rounded-lg border bg-card p-4">
        <h3 className="mb-3 text-sm font-semibold">Add KB article</h3>
        <div className="grid gap-2 md:grid-cols-4">
          <Input
            placeholder="Title"
            value={draft.title}
            onChange={(e) => setDraft({ ...draft, title: e.target.value })}
          />
          <Select
            value={draft.source}
            onValueChange={(v) => setDraft({ ...draft, source: v })}
          >
            <SelectTrigger>
              <SelectValue />
            </SelectTrigger>
            <SelectContent>
              {SOURCE_OPTIONS.map((s) => (
                <SelectItem key={s.value} value={s.value}>
                  {s.label}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
          <Input
            placeholder="Tags (comma separated)"
            value={draft.tags}
            onChange={(e) => setDraft({ ...draft, tags: e.target.value })}
          />
          <Button onClick={add} disabled={!draft.title.trim() || !draft.content.trim()}>
            <Plus className="mr-1 h-4 w-4" /> Add
          </Button>
        </div>
        <Textarea
          placeholder="Article content (markdown/plain text)…"
          value={draft.content}
          onChange={(e) => setDraft({ ...draft, content: e.target.value })}
          rows={5}
          className="mt-2"
        />
      </div>

      {loading ? (
        <LoadingRow />
      ) : (
        <div className="rounded-lg border bg-card divide-y">
          {items.length === 0 && (
            <div className="p-4 text-sm text-muted-foreground">
              No articles yet.
            </div>
          )}
          {items.map((a) => {
            const expanded = expandedId === a.id;
            return (
              <div key={a.id} className="p-3 text-sm">
                <div className="flex flex-wrap items-center gap-2">
                  <Input
                    value={a.title}
                    onChange={(e) =>
                      setItems((p) =>
                        p.map((x) =>
                          x.id === a.id ? { ...x, title: e.target.value } : x
                        )
                      )
                    }
                    className="flex-1 min-w-[200px]"
                  />
                  <Badge variant="outline">{a.source}</Badge>
                  <div className="flex items-center gap-1.5">
                    <Switch
                      checked={a.is_published}
                      onCheckedChange={(v) =>
                        setItems((p) =>
                          p.map((x) =>
                            x.id === a.id ? { ...x, is_published: v } : x
                          )
                        )
                      }
                    />
                    <span className="text-xs text-muted-foreground">
                      Published
                    </span>
                  </div>
                  <Button
                    size="sm"
                    variant="ghost"
                    onClick={() => setExpandedId(expanded ? null : a.id)}
                  >
                    {expanded ? "Collapse" : "Edit"}
                  </Button>
                  <Button
                    size="sm"
                    variant="outline"
                    onClick={() => save(a)}
                  >
                    <Save className="mr-1 h-3.5 w-3.5" /> Save
                  </Button>
                  <Button
                    size="sm"
                    variant="ghost"
                    className="text-red-600"
                    onClick={() => del(a.id)}
                  >
                    <Trash2 className="h-3.5 w-3.5" />
                  </Button>
                </div>
                {expanded && (
                  <div className="mt-2 grid gap-2 md:grid-cols-2">
                    <Input
                      placeholder="Tags"
                      value={a.tags || ""}
                      onChange={(e) =>
                        setItems((p) =>
                          p.map((x) =>
                            x.id === a.id ? { ...x, tags: e.target.value } : x
                          )
                        )
                      }
                    />
                    <Input
                      placeholder="Source URL"
                      value={a.source_url || ""}
                      onChange={(e) =>
                        setItems((p) =>
                          p.map((x) =>
                            x.id === a.id
                              ? { ...x, source_url: e.target.value }
                              : x
                          )
                        )
                      }
                    />
                    <Textarea
                      value={a.content}
                      rows={8}
                      onChange={(e) =>
                        setItems((p) =>
                          p.map((x) =>
                            x.id === a.id
                              ? { ...x, content: e.target.value }
                              : x
                          )
                        )
                      }
                      className="md:col-span-2"
                    />
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function LoadingRow() {
  return (
    <div className="flex items-center gap-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground">
      <Loader2 className="h-4 w-4 animate-spin" /> Loading…
    </div>
  );
}

function ErrorNote({ message }: { message: string }) {
  return (
    <div className="rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-800 flex items-center gap-2">
      <AlertCircle className="h-4 w-4" /> {message}
    </div>
  );
}