"use client";

/**
 * /admin/sessions/[sessionId] — Session Detail
 *
 * Full journey reconstruction for a single session: session metadata
 * card + chronological timeline of every TrackedEvent + SiteConversion
 * + repeat-interaction summary (phone numbers clicked multiple times
 * in the session — an important UX / operational signal).
 *
 * Requires brandId in the querystring to scope the lookup.
 */

import { useCallback, useEffect, useState } from "react";
import Link from "next/link";
import {
  Footprints, ArrowLeft, Clock, Eye, Globe, Monitor, Smartphone,
  Tablet, FileCheck, CalendarCheck, Phone, MousePointerClick,
  ExternalLink, FileEdit, Zap, CheckCircle2, Play, Flag,
  AlertCircle, RefreshCw,
  // Added for the expanded timeline event-type coverage.
  Mail, Copy, MousePointer, ShoppingCart, CreditCard, MessageSquare,
  BarChart3, Download, FileText, Printer, Share2, Star, Users,
  ArrowLeftCircle, Route, BookOpen, Type, Layers, Search, Activity,
  AlertTriangle, FormInput, ArrowUp, Keyboard, FileX, Accessibility,
} from "lucide-react";
import {
  PageHeader, Card, CardHeader, EmptyState, Skel,
} from "@/components/admin";

interface TimelineEntry {
  kind: "event" | "conversion";
  id: string;
  type: string;
  canonicalType: string | null;
  timestamp: string;
  pageUrl: string;
  metadata: Record<string, unknown>;
  formFields?: Record<string, unknown> | null;
}

interface SessionDetail {
  session: {
    sessionId: string;
    internalId: string;
    brandId: string;
    startedAt: string;
    lastSeenAt: string;
    durationMs: number;
    pageCount: number;
    entryPage: string;
    referrer: string | null;
    source: string;
    medium: string | null;
    campaign: string | null;
    country: string | null;
    device: string | null;
    userAgent: string | null;
    browser?: string | null;
    os?: string | null;
    city?: string | null;
    region?: string | null;
    ipAddress?: string | null;
  };
  timeline: TimelineEntry[];
  aggregates: {
    eventCountByType: Record<string, number>;
    conversionCountByCanonical: Record<string, number>;
    interactionCountByCanonical: Record<string, number>;
    phoneClickCounts: Record<string, number>;
    uniquePhoneNumbers: number;
    totalEvents: number;
    totalConversions: number;
  };
  error?: string;
}

// ─── Helpers ──────────────────────────────────────────────────────

function formatDuration(ms: number): string {
  if (ms < 1000) return "<1s";
  const s = Math.floor(ms / 1000);
  if (s < 60) return `${s}s`;
  const m = Math.floor(s / 60);
  const rs = s % 60;
  if (m < 60) return `${m}m ${rs}s`;
  const h = Math.floor(m / 60);
  const rm = m % 60;
  return `${h}h ${rm}m`;
}

function formatTime(iso: string): string {
  try {
    return new Date(iso).toLocaleTimeString(undefined, {
      hour: "2-digit", minute: "2-digit", second: "2-digit",
    });
  } catch { return iso; }
}

function formatDate(iso: string): string {
  try {
    return new Date(iso).toLocaleString(undefined, {
      month: "short", day: "numeric", hour: "2-digit", minute: "2-digit",
    });
  } catch { return iso; }
}

function extractPath(url: string): string {
  try { return new URL(url).pathname || url; } catch { return url; }
}

// Colour palette for timeline chips. Keyed by semantic name so the
// event-type table below can stay concise and every event with
// the same semantic colour renders identically without drift.
const TIMELINE_COLORS: Record<string, { color: string; bg: string; border: string }> = {
  blue:    { color: "text-blue-700",    bg: "bg-blue-50",    border: "border-blue-300" },
  green:   { color: "text-emerald-700", bg: "bg-emerald-50", border: "border-emerald-300" },
  purple:  { color: "text-violet-700",  bg: "bg-violet-50",  border: "border-violet-300" },
  orange:  { color: "text-orange-700",  bg: "bg-orange-50",  border: "border-orange-300" },
  teal:    { color: "text-teal-700",    bg: "bg-teal-50",    border: "border-teal-300" },
  gray:    { color: "text-slate-600",   bg: "bg-slate-100",  border: "border-slate-300" },
  slate:   { color: "text-slate-700",   bg: "bg-slate-50",   border: "border-slate-200" },
  red:     { color: "text-red-700",     bg: "bg-red-50",     border: "border-red-300" },
  yellow:  { color: "text-yellow-700",  bg: "bg-yellow-50",  border: "border-yellow-300" },
  amber:   { color: "text-amber-700",   bg: "bg-amber-50",   border: "border-amber-300" },
  cyan:    { color: "text-cyan-700",    bg: "bg-cyan-50",    border: "border-cyan-300" },
  indigo:  { color: "text-indigo-700",  bg: "bg-indigo-50",  border: "border-indigo-300" },
};

type TimelineIconEntry = { Icon: typeof Phone; color: keyof typeof TIMELINE_COLORS; label: string };

// Single source of truth for every non-canonical event type
// we know about. Canonical-conversion types are handled in
// the switch at the top of entryIcon so they always win over
// the raw-type lookup (e.g. a click_to_call whose canonical is
// phone_call always renders as the phone-call chip, even though
// the raw type is also in this table).
const EVENT_TYPE_META: Record<string, TimelineIconEntry> = {
  // ── Core navigation / session ──
  page_view:                 { Icon: Eye,                  color: "gray",    label: "Page View" },
  session_start:             { Icon: Play,                 color: "green",   label: "Session Start" },
  outbound_click:            { Icon: ExternalLink,         color: "cyan",    label: "Outbound Click" },
  booking_step:              { Icon: CheckCircle2,         color: "teal",    label: "Booking Step" },
  booking_widget_opened:     { Icon: Zap,                  color: "orange",  label: "Booking Widget Opened" },
  plugin_detected:           { Icon: Zap,                  color: "indigo",  label: "Plugin Detected" },
  form_iframe_present:       { Icon: Zap,                  color: "indigo",  label: "Form Iframe Present" },
  chat_opened:               { Icon: MousePointerClick,    color: "cyan",    label: "Chat Opened" },
  chat_widget_detected:      { Icon: MousePointerClick,    color: "cyan",    label: "Chat Widget Detected" },
  chat_message_sent:         { Icon: MousePointerClick,    color: "cyan",    label: "Chat Message Sent" },
  form_intent:               { Icon: MousePointerClick,    color: "orange",  label: "Form Intent" },
  // ── Email ──
  email_click:               { Icon: Mail,                 color: "blue",    label: "Email Click" },
  email_copied:              { Icon: Copy,                 color: "blue",    label: "Email Copied" },
  newsletter_signup:         { Icon: Mail,                 color: "green",   label: "Newsletter Signup" },
  // ── E-commerce ──
  product_view:              { Icon: Eye,                  color: "purple",  label: "Product View" },
  product_click:             { Icon: MousePointer,         color: "purple",  label: "Product Click" },
  add_to_cart:               { Icon: ShoppingCart,         color: "orange",  label: "Add to Cart" },
  cart_view:                 { Icon: ShoppingCart,         color: "gray",    label: "Cart View" },
  cart_update:               { Icon: RefreshCw,            color: "gray",    label: "Cart Update" },
  cart_abandoned:            { Icon: ShoppingCart,         color: "red",     label: "Cart Abandoned" },
  checkout_started:          { Icon: CreditCard,           color: "teal",    label: "Checkout Started" },
  checkout_step:             { Icon: CreditCard,           color: "teal",    label: "Checkout Step" },
  payment_method_selected:   { Icon: CreditCard,           color: "green",   label: "Payment Selected" },
  purchase_completed:        { Icon: CheckCircle2,         color: "green",   label: "Purchase Completed" },
  purchase:                  { Icon: CheckCircle2,         color: "green",   label: "Purchase" },
  // ── Phone / SMS ──
  phone_number_visible:      { Icon: Phone,                color: "gray",    label: "Phone Visible" },
  sms_click:                 { Icon: MessageSquare,        color: "blue",    label: "SMS Click" },
  sms_submit:                { Icon: MessageSquare,        color: "blue",    label: "SMS Submit" },
  call_tracking_detected:    { Icon: Phone,                color: "amber",   label: "Call Tracking Detected" },
  // ── Video ──
  video_play:                { Icon: Play,                 color: "purple",  label: "Video Play" },
  video_progress:            { Icon: Play,                 color: "purple",  label: "Video Progress" },
  video_complete:            { Icon: CheckCircle2,         color: "green",   label: "Video Complete" },
  video_engagement:          { Icon: BarChart3,            color: "purple",  label: "Video Engagement" },
  // ── Files / docs ──
  file_download:             { Icon: Download,             color: "slate",   label: "File Download" },
  pdf_opened:                { Icon: FileText,             color: "red",     label: "PDF Opened" },
  pdf_time_spent:            { Icon: Clock,                color: "red",     label: "PDF Time Spent" },
  page_printed:              { Icon: Printer,              color: "gray",    label: "Page Printed" },
  // ── Social sharing / proof ──
  social_share:              { Icon: Share2,               color: "blue",    label: "Social Share" },
  content_copied:            { Icon: Copy,                 color: "gray",    label: "Content Copied" },
  review_widget_visible:     { Icon: Star,                 color: "yellow",  label: "Review Widget Visible" },
  review_widget_clicked:     { Icon: Star,                 color: "yellow",  label: "Review Widget Clicked" },
  social_proof_visible:      { Icon: Users,                color: "green",   label: "Social Proof Visible" },
  social_proof_attention:    { Icon: Users,                color: "green",   label: "Social Proof Attention" },
  // ── Competitor ──
  competitor_visit:          { Icon: ExternalLink,         color: "red",     label: "Competitor Visit" },
  competitor_referral:       { Icon: ArrowLeftCircle,      color: "amber",   label: "From Competitor" },
  competitive_journey:       { Icon: Route,                color: "red",     label: "Competitive Journey" },
  // ── Content consumption ──
  content_engagement:        { Icon: BookOpen,             color: "teal",    label: "Content Engagement" },
  text_selected:             { Icon: Type,                 color: "gray",    label: "Text Selected" },
  tab_pattern:               { Icon: Layers,               color: "gray",    label: "Tab Pattern" },
  site_search:               { Icon: Search,               color: "blue",    label: "Site Search" },
  // ── Performance / errors ──
  web_vitals:                { Icon: Activity,             color: "green",   label: "Web Vitals" },
  slow_interaction:          { Icon: AlertTriangle,        color: "amber",   label: "Slow Interaction" },
  js_error:                  { Icon: AlertCircle,          color: "red",     label: "JS Error" },
  resource_error:            { Icon: AlertCircle,          color: "orange",  label: "Resource Error" },
  // ── UX frustration ──
  rage_click:                { Icon: MousePointer,         color: "red",     label: "Rage Click" },
  dead_click:                { Icon: MousePointer,         color: "orange",  label: "Dead Click" },
  error_shown:               { Icon: AlertTriangle,        color: "red",     label: "Error Shown" },
  form_abandoned:            { Icon: FormInput,            color: "amber",   label: "Form Abandoned" },
  scroll_bounce:             { Icon: ArrowUp,              color: "red",     label: "Scroll Bounce" },
  // ── Accessibility + navigation ──
  keyboard_navigation:       { Icon: Keyboard,             color: "blue",    label: "Keyboard Navigation" },
  navigation_pattern:        { Icon: ArrowLeft,            color: "gray",    label: "Navigation Pattern" },
  page_not_found:            { Icon: FileX,                color: "red",     label: "404 Page" },
  accessibility_context:     { Icon: Accessibility,        color: "blue",    label: "Accessibility Context" },
  // ── Scheduling ──
  calendar_add:              { Icon: CalendarCheck,        color: "blue",    label: "Add to Calendar" },
  appointment_modify_intent: { Icon: RefreshCw,            color: "amber",   label: "Appointment Modify Intent" },
  availability_check:        { Icon: CalendarCheck,        color: "gray",    label: "Availability Check" },
  waitlist_signup:           { Icon: Clock,                color: "amber",   label: "Waitlist Signup" },
  // ── Cross-session ──
  return_visit:              { Icon: ArrowLeftCircle,      color: "teal",    label: "Return Visit" },
  conversion_path_complete:  { Icon: Route,                color: "green",   label: "Conversion Path" },
  multi_session_conversion:  { Icon: Route,                color: "green",   label: "Multi-Session Conversion" },
};

// Timeline entry → icon + colour + label. Canonical-conversion
// types win over the raw type lookup so a click_to_call whose
// server-side canonical is phone_call renders with the phone-call
// chip semantics even when the raw type is also in the table.
function entryIcon(entry: TimelineEntry): { Icon: typeof Phone; color: string; bg: string; border: string; label: string } {
  const canon = entry.canonicalType;
  const t = entry.type;
  if (canon === "appointment_booked") {
    return { Icon: CalendarCheck, color: "text-emerald-700", bg: "bg-emerald-50", border: "border-emerald-300", label: "Appointment Booked" };
  }
  if (canon === "form_submitted") {
    return { Icon: FileCheck, color: "text-blue-700", bg: "bg-blue-50", border: "border-blue-300", label: "Form Submitted" };
  }
  if (canon === "phone_call") {
    return { Icon: Phone, color: "text-violet-700", bg: "bg-violet-50", border: "border-violet-300", label: "Phone Clicked" };
  }
  if (canon === "email_contact") {
    return { Icon: Mail, color: "text-blue-700", bg: "bg-blue-50", border: "border-blue-300", label: "Email Contact" };
  }
  if (canon === "sms_contact") {
    return { Icon: MessageSquare, color: "text-violet-700", bg: "bg-violet-50", border: "border-violet-300", label: "SMS Contact" };
  }
  if (canon === "appointment_attempted") {
    return { Icon: CalendarCheck, color: "text-teal-700", bg: "bg-teal-50", border: "border-teal-300", label: "Booking Submitted" };
  }
  if (canon === "appointment_intent") {
    return { Icon: MousePointerClick, color: "text-orange-700", bg: "bg-orange-50", border: "border-orange-300", label: "Appointment Intent" };
  }
  if (canon === "form_started") {
    return { Icon: FileEdit, color: "text-slate-600", bg: "bg-slate-100", border: "border-slate-300", label: "Form Started" };
  }
  // Lookup by raw type — the EVENT_TYPE_META table covers every
  // event public/t.js emits today plus legacy raw conversion names.
  const meta = EVENT_TYPE_META[t];
  if (meta) {
    const palette = TIMELINE_COLORS[meta.color] ?? TIMELINE_COLORS.gray;
    return { Icon: meta.Icon, color: palette.color, bg: palette.bg, border: palette.border, label: meta.label };
  }
  // Unknown event type — humanise the raw string so the fallback
  // chip at least reads better than "form_foobar_baz".
  return {
    Icon: Flag,
    color: "text-slate-500",
    bg: "bg-slate-50",
    border: "border-slate-200",
    label: t.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()),
  };
}

// Human-readable description for a timeline entry. Pulls from
// metadata when useful (CTA text, step number, phone number).
function entryDescription(entry: TimelineEntry): string {
  const m = entry.metadata ?? {};
  const path = extractPath(entry.pageUrl);

  if (entry.type === "session_start") {
    const src = typeof m.utm_source === "string" ? m.utm_source : "direct";
    return `${path} (from ${src})`;
  }
  if (entry.type === "page_view") {
    const title = typeof m.title === "string" ? m.title.slice(0, 80) : "";
    return title ? `${path} — ${title}` : path;
  }
  if (entry.type === "click_to_call" || entry.canonicalType === "phone_call") {
    const phone = typeof m.phone_number === "string" ? m.phone_number
      : typeof m.phone_normalized === "string" ? m.phone_normalized
      : null;
    return phone ? `${phone} · ${path}` : path;
  }
  if (entry.type === "outbound_click") {
    const domain = typeof m.link_domain === "string" ? m.link_domain : null;
    return domain ? `→ ${domain} from ${path}` : path;
  }
  if (entry.type === "form_start") {
    const field = typeof m.field_name === "string" ? m.field_name : "field";
    return `focused "${field}" on ${path}`;
  }
  if (entry.type === "booking_step") {
    const step = typeof m.step === "string" ? m.step : "?";
    const source = typeof m.source === "string" ? m.source : "plugin";
    const stepName = typeof m.stepName === "string" ? ` — ${m.stepName}` : "";
    return `${source} step ${step}${stepName} · ${path}`;
  }
  if (entry.type === "booking_widget_opened" || entry.type === "plugin_detected") {
    // Handle new consolidated payload { plugins: [...] } and legacy flat shape.
    let source: string;
    if (typeof m.source === "string") {
      source = m.source;
    } else if (Array.isArray(m.plugins) && m.plugins.length > 0) {
      const first = m.plugins[0] as Record<string, unknown>;
      source = typeof first.plugin === "string" ? first.plugin : "plugin";
    } else {
      source = typeof m.plugin === "string" ? m.plugin : "plugin";
    }
    return `${source} · ${path}`;
  }
  if (entry.type === "appointment_intent" || entry.type === "form_intent") {
    const cta = typeof m.cta_text === "string" ? m.cta_text : "CTA";
    return `"${cta}" on ${path}`;
  }
  if (entry.kind === "conversion") {
    const source = typeof m.formProvider === "string" ? m.formProvider
      : (entry.formFields && typeof entry.formFields._source === "string" ? entry.formFields._source : null);
    const provider = source ? ` via ${source}` : "";
    return `${path}${provider}`;
  }
  return path;
}

function DeviceIcon({ device }: { device: string | null }) {
  if (device === "mobile")  return <Smartphone className="w-3 h-3 text-slate-400" />;
  if (device === "tablet")  return <Tablet className="w-3 h-3 text-slate-400" />;
  return <Monitor className="w-3 h-3 text-slate-400" />;
}

// ─── Page ──────────────────────────────────────────────────────────

export default function SessionDetailPage({
  params, searchParams,
}: {
  params: { sessionId: string };
  searchParams: { brandId?: string };
}) {
  // Next.js 14 App Router — params / searchParams are plain objects,
  // not Promises. (The React `use()` unwrap is a Next 15 pattern and
  // blew up at render with a React #438 hook-order error here.)
  const sessionId = params.sessionId;
  const brandId = searchParams.brandId;
  const [data, setData] = useState<SessionDetail | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const load = useCallback(async () => {
    if (!brandId) { setLoading(false); return; }
    setLoading(true);
    setError(null);
    try {
      const url = `/api/admin/sessions/${encodeURIComponent(sessionId)}?brandId=${encodeURIComponent(brandId)}`;
      const r = await fetch(url);
      if (!r.ok) {
        // Surface the server-provided error if there is one; otherwise
        // fall back to a plain status-code message.
        let msg = `Request failed (${r.status})`;
        try {
          const body = await r.json();
          if (body && typeof body.error === "string") msg = body.error;
        } catch { /* non-JSON body — keep the status message */ }
        setError(msg);
        setData(null);
        return;
      }
      const d: SessionDetail = await r.json();
      if (d && typeof d.error === "string") {
        setError(d.error);
        setData(null);
        return;
      }
      setData(d);
    } catch (e) {
      setError(e instanceof Error ? e.message : "Network error");
      setData(null);
    } finally {
      setLoading(false);
    }
  }, [sessionId, brandId]);

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

  if (!brandId) {
    return (
      <div className="space-y-4">
        <PageHeader
          title="Session detail"
          subtitle="brandId query param required"
          icon={Footprints}
          actions={
            <Link href="/admin/sessions" className="inline-flex items-center gap-1 text-[11px] text-slate-600">
              <ArrowLeft className="w-3 h-3" />Back to sessions
            </Link>
          }
        />
        <Card>
          <EmptyState
            icon={AlertCircle}
            title="Missing brandId"
            description="Open this page via a session row in the Session Explorer — the URL needs ?brandId=<cuid>."
          />
        </Card>
      </div>
    );
  }

  if (loading) {
    return <SessionDetailSkeleton brandId={brandId} />;
  }

  if (error || !data) {
    return (
      <div className="space-y-4">
        <PageHeader
          title="Session detail"
          subtitle={error ?? "Not found"}
          icon={Footprints}
          actions={
            <Link href={`/admin/sessions?brandId=${encodeURIComponent(brandId)}`} className="inline-flex items-center gap-1 text-[11px] text-slate-600">
              <ArrowLeft className="w-3 h-3" />Back to sessions
            </Link>
          }
        />
        <Card>
          <div className="px-5 py-8 flex flex-col items-center text-center gap-3">
            <div className="w-10 h-10 rounded-full bg-rose-50 text-rose-600 flex items-center justify-center">
              <AlertCircle className="w-5 h-5" />
            </div>
            <div>
              <p className="text-sm font-semibold text-slate-900">Couldn&apos;t load session</p>
              <p className="text-xs text-slate-500 mt-0.5 max-w-md">
                {error ?? "The session was not found for this brand."}
              </p>
            </div>
            <button
              type="button"
              onClick={load}
              className="inline-flex items-center gap-1.5 px-3 py-1.5 text-[11px] font-semibold rounded-md bg-slate-900 text-white hover:bg-slate-700"
            >
              <RefreshCw className="w-3 h-3" /> Retry
            </button>
          </div>
        </Card>
      </div>
    );
  }

  const { session, timeline, aggregates } = data;
  const phoneClickCounts = aggregates?.phoneClickCounts ?? {};
  const eventCountByType = aggregates?.eventCountByType ?? {};
  const conversionCountByCanonical = aggregates?.conversionCountByCanonical ?? {};
  const repeatClicks = Object.entries(phoneClickCounts).filter(([, count]) => count > 1);

  return (
    <div className="space-y-6">
      <PageHeader
        title="Session detail"
        subtitle={session.sessionId}
        icon={Footprints}
        actions={
          <Link
            href={`/admin/sessions?brandId=${encodeURIComponent(brandId)}`}
            className="inline-flex items-center gap-1 px-3 py-1.5 text-[11px] font-semibold rounded-md bg-white border border-slate-200 text-slate-700 hover:bg-slate-50"
          >
            <ArrowLeft className="w-3 h-3" />Back to sessions
          </Link>
        }
      />

      {/* ── Session header card ── */}
      <Card>
        <div className="p-5 grid grid-cols-2 md:grid-cols-4 gap-4 text-xs">
          <div>
            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-500 mb-1">Duration</p>
            <p className="text-lg font-bold text-slate-900 tabular-nums inline-flex items-center gap-1">
              <Clock className="w-3.5 h-3.5 text-slate-400" />{formatDuration(session.durationMs)}
            </p>
            <p className="text-[10px] text-slate-400 mt-0.5">{formatDate(session.startedAt)}</p>
          </div>
          <div>
            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-500 mb-1">Pages viewed</p>
            <p className="text-lg font-bold text-slate-900 tabular-nums inline-flex items-center gap-1">
              <Eye className="w-3.5 h-3.5 text-slate-400" />{session.pageCount}
            </p>
            <p className="text-[10px] text-slate-400 mt-0.5 truncate max-w-[240px]">entry: {extractPath(session.entryPage)}</p>
          </div>
          <div>
            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-500 mb-1">Source</p>
            <p className="text-lg font-bold text-slate-900 inline-flex items-center gap-1">{session.source}</p>
            <p className="text-[10px] text-slate-400 mt-0.5">
              {session.medium ?? "—"}{session.campaign ? ` · ${session.campaign}` : ""}
            </p>
          </div>
          <div>
            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-500 mb-1">Device / location</p>
            <p className="text-lg font-bold text-slate-900 inline-flex items-center gap-1 capitalize">
              <DeviceIcon device={session.device} />
              {session.device ?? "Unknown"}
            </p>
            <p className="text-[10px] text-slate-400 mt-0.5 inline-flex items-center gap-1">
              <Globe className="w-2.5 h-2.5" />
              {[session.city, session.region, session.country].filter(Boolean).join(", ") || "unknown"}
            </p>
          </div>
        </div>

        {/* Extended device + location detail — browser / OS / IP.
            Only rendered when at least one value is present so
            historical sessions (captured before the columns existed)
            collapse instead of showing an empty card. */}
        {(session.browser || session.os || session.ipAddress || session.userAgent) && (
          <div className="border-t border-slate-100 px-5 py-4 grid grid-cols-2 md:grid-cols-4 gap-4 text-[11px]">
            <div>
              <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-0.5">Browser</p>
              <p className="text-slate-800 font-medium">{session.browser ?? "—"}</p>
            </div>
            <div>
              <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-0.5">OS</p>
              <p className="text-slate-800 font-medium">{session.os ?? "—"}</p>
            </div>
            <div>
              <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-0.5">IP (admin)</p>
              <p className="text-slate-800 font-mono text-[10px] truncate" title={session.ipAddress ?? undefined}>
                {session.ipAddress ? (session.ipAddress.length > 18 ? session.ipAddress.slice(0, 18) + "…" : session.ipAddress) : "—"}
              </p>
            </div>
            <div>
              <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-0.5">User-Agent</p>
              <p className="text-slate-800 font-mono text-[10px] truncate" title={session.userAgent ?? undefined}>
                {session.userAgent ?? "—"}
              </p>
            </div>
          </div>
        )}
      </Card>

      {/* ── Interaction summary cards ── */}
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
        <SummaryCard
          icon={MousePointerClick} color="text-slate-600"
          label="Total events"
          value={aggregates?.totalEvents ?? 0}
          sub={`${Object.keys(eventCountByType).length} types`}
        />
        <SummaryCard
          icon={CheckCircle2} color="text-emerald-600"
          label="Conversions"
          value={aggregates?.totalConversions ?? 0}
          sub={(aggregates?.totalConversions ?? 0) === 0 ? "no conversions" : "tier-1 + tier-2"}
        />
        <SummaryCard
          icon={Phone} color="text-violet-600"
          label="Phone clicks"
          value={Object.values(phoneClickCounts).reduce((a, b) => a + b, 0)}
          sub={`${aggregates?.uniquePhoneNumbers ?? 0} unique number${(aggregates?.uniquePhoneNumbers ?? 0) === 1 ? "" : "s"}`}
        />
        <SummaryCard
          icon={FileEdit} color="text-blue-600"
          label="Form interactions"
          value={(eventCountByType.form_start ?? 0) + (conversionCountByCanonical.form_submitted ?? 0)}
          sub={`${eventCountByType.form_start ?? 0} start · ${conversionCountByCanonical.form_submitted ?? 0} submit`}
        />
      </div>

      {/* ── Repeat phone clicks — UX signal ── */}
      {repeatClicks.length > 0 && (
        <Card>
          <CardHeader
            title="Repeat interactions"
            right={<span className="text-[10px] text-slate-400">same action, multiple times</span>}
          />
          <div className="divide-y divide-slate-100">
            {(repeatClicks ?? []).map(([phone, count]) => (
              <div key={phone} className="px-5 py-3 flex items-center gap-3">
                <Phone className="w-4 h-4 text-violet-500 shrink-0" />
                <p className="font-mono text-sm font-semibold text-slate-900">{phone}</p>
                <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded bg-amber-50 text-amber-700 border border-amber-200">
                  Clicked {count} times
                </span>
                <p className="text-[10px] text-slate-400 ml-auto">
                  {count} clicks in one session may indicate a broken tel: link, voicemail retries, or number comparison.
                </p>
              </div>
            ))}
          </div>
        </Card>
      )}

      {/* ── Timeline ── */}
      <Card>
        <CardHeader
          title="Interaction timeline"
          right={<span className="text-[10px] text-slate-400">{(timeline ?? []).length} events · chronological</span>}
        />
        {(timeline ?? []).length === 0 ? (
          <EmptyState
            icon={Footprints}
            title="No events in this session"
            description="The session row exists but no events were captured — likely a bot hit or an ad-blocker."
          />
        ) : (
          <ol className="relative">
            {(timeline ?? []).map((entry, i) => {
              const { Icon, color, bg, border, label } = entryIcon(entry);
              const isLast = i === (timeline ?? []).length - 1;
              return (
                <li key={entry.id} className="relative px-5 py-2.5 flex gap-3">
                  {/* Connecting line */}
                  {!isLast && (
                    <span className="absolute left-[34px] top-8 bottom-[-0.5rem] w-px bg-slate-200" />
                  )}
                  {/* Icon dot */}
                  <span className={`relative z-10 w-6 h-6 rounded-full ${bg} ${border} border-2 flex items-center justify-center shrink-0`}>
                    <Icon className={`w-3 h-3 ${color}`} />
                  </span>
                  {/* Content */}
                  <div className="flex-1 min-w-0">
                    <div className="flex items-baseline gap-2 flex-wrap">
                      <span className="text-[10px] font-mono text-slate-400 tabular-nums shrink-0">
                        {formatTime(entry.timestamp)}
                      </span>
                      <span className={`inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border ${bg} ${color} ${border}`}>
                        {label}
                      </span>
                      {entry.kind === "conversion" && (
                        <span className="text-[9px] font-bold uppercase tracking-widest text-emerald-700">conversion</span>
                      )}
                    </div>
                    <p className="text-xs text-slate-700 mt-0.5 truncate">{entryDescription(entry)}</p>
                  </div>
                </li>
              );
            })}
          </ol>
        )}
        <div className="px-5 py-3 border-t border-slate-100 bg-slate-50/40 text-[10px] text-slate-500">
          Session ended at {formatDate(session.lastSeenAt)} · {formatDuration(session.durationMs)} total
        </div>
      </Card>
    </div>
  );
}

/**
 * Full-page skeleton that mirrors the loaded layout: header card with
 * 4 metric cells, a row of 4 summary cards, and a timeline with a few
 * placeholder rows. Keeps the visual jump between states small.
 */
function SessionDetailSkeleton({ brandId }: { brandId: string }) {
  return (
    <div className="space-y-6">
      <PageHeader
        title="Session detail"
        subtitle="Loading…"
        icon={Footprints}
        actions={
          <Link
            href={`/admin/sessions?brandId=${encodeURIComponent(brandId)}`}
            className="inline-flex items-center gap-1 px-3 py-1.5 text-[11px] font-semibold rounded-md bg-white border border-slate-200 text-slate-700 hover:bg-slate-50"
          >
            <ArrowLeft className="w-3 h-3" />Back to sessions
          </Link>
        }
      />
      <Card>
        <div className="p-5 grid grid-cols-2 md:grid-cols-4 gap-4">
          {Array.from({ length: 4 }).map((_, i) => (
            <div key={i} className="space-y-2">
              <Skel className="h-2.5 w-20 bg-slate-100 rounded" />
              <Skel className="h-6 w-24 bg-slate-200 rounded" />
              <Skel className="h-2.5 w-32 bg-slate-100 rounded" />
            </div>
          ))}
        </div>
      </Card>
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
        {Array.from({ length: 4 }).map((_, i) => (
          <Card key={i}>
            <div className="p-4 space-y-2">
              <Skel className="h-2.5 w-20 bg-slate-100 rounded" />
              <Skel className="h-7 w-16 bg-slate-200 rounded" />
              <Skel className="h-2.5 w-28 bg-slate-100 rounded" />
            </div>
          </Card>
        ))}
      </div>
      <Card>
        <div className="px-5 py-4 border-b border-slate-100">
          <Skel className="h-3 w-40 bg-slate-200 rounded" />
        </div>
        <ol className="relative">
          {Array.from({ length: 5 }).map((_, i) => (
            <li key={i} className="px-5 py-2.5 flex gap-3 items-center">
              <Skel className="h-6 w-6 rounded-full bg-slate-100" />
              <div className="flex-1 space-y-1.5">
                <Skel className="h-2.5 w-48 bg-slate-200 rounded" />
                <Skel className="h-2.5 w-64 bg-slate-100 rounded" />
              </div>
            </li>
          ))}
        </ol>
      </Card>
    </div>
  );
}

function SummaryCard({
  icon: Icon, label, value, sub, color,
}: {
  icon: typeof Phone;
  label: string;
  value: number;
  sub: string;
  color: string;
}) {
  return (
    <Card>
      <div className="p-4">
        <div className="flex items-center justify-between mb-1">
          <p className="text-[10px] font-bold uppercase tracking-widest text-slate-500">{label}</p>
          <Icon className={`w-3.5 h-3.5 ${color}`} />
        </div>
        <p className="text-2xl font-bold text-slate-900 tabular-nums">{value.toLocaleString()}</p>
        <p className="text-[10px] text-slate-400 mt-0.5">{sub}</p>
      </div>
    </Card>
  );
}
