"use client";

/**
 * Keyword Battles
 * ────────────────
 * Live SERP Domain Intersection — every keyword the brand and a
 * selected competitor BOTH rank for in the top 100, with each domain's
 * actual position, the page that's ranking, and metrics (volume, CPC,
 * difficulty). One row per keyword. Rendered as the third tab in the
 * Content Analysis card alongside Content Gaps + Head-to-Head.
 *
 * Data flow:
 *   1. User picks a competitor from the select.
 *   2. We hit /api/competitors/[id]/keyword-overlap (~$0.02 per call).
 *   3. Cache the result client-side keyed by competitorId so switching
 *      back to a previously-viewed competitor is free.
 *   4. Status badges color-code the strategic situation:
 *        winning  = brand outranks competitor
 *        losing   = competitor outranks brand (highest priority)
 *        gap      = competitor ranks, brand doesn't
 *   5. Action buttons hand off the keyword to either the Rank Tracker
 *      (Track) or the SEO Content Brief (Create).
 */

import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
import {
  Swords, Loader2, ExternalLink, ArrowUpRight, AlertCircle, Search,
  Plus, FileText, Filter, Bot,
} from "lucide-react";
import { useAiIndicators } from "@/hooks/useAiIndicators";
import { AiBadge } from "@/components/ai-badge";

interface OverlapRow {
  keyword: string;
  volume: number | null;
  cpc: number | null;
  difficulty: number | null;
  brandPosition: number | null;
  brandUrl: string | null;
  competitorPosition: number | null;
  competitorUrl: string | null;
  status: "winning" | "losing" | "brand_only" | "competitor_only" | "unknown";
  serpFeatures: string[];
}

interface OverlapResponse {
  rows: OverlapRow[];
  totalCount: number;
  costUsd: number;
  /** Where the data came from:
   *    live         — live Domain Intersection (has positions + URLs)
   *    ai_estimate  — AI fallback when the SERP provider returned 0 rows
   *    none         — nothing usable; note explains why
   */
  source?: "live" | "ai_estimate" | "ai_estimate_enriched" | "none";
  enrichedCount?: number;
  /** Server-attached explanation for empty/fallback states. */
  note?: string;
  configured?: boolean;
  error?: string;
  /** Cache metadata — present when the server returned a row from
   *  its 7-day SiteEvent cache (see
   *  /api/competitors/[id]/keyword-overlap). Drives the "Cached"
   *  badge + Refresh button on the client. */
  cached?: boolean;
  cachedAgeHours?: number;
}

interface CompetitorOption {
  id: string;
  domain: string;
  name?: string;
}

interface ContentGapKeyword {
  keyword: string;
  competitorUrl: string;
  competitorDomain: string;
  wordCount: number | null;
  confidence: number;
}

interface Props {
  brandId: string;
  brandDomain: string;
  competitors: CompetitorOption[];
  contentGapKeywords?: ContentGapKeyword[];
  onTrackKeyword?: (keyword: string) => void;
  onCreateBrief?: (keyword: string) => void;
  onCreateLlmoBrief?: (keyword: string, competitorDomain?: string) => void;
}

type StatusFilter = "all" | "winning" | "losing" | "competitor_only" | "content_gap";
type SortKey = "volume" | "diff" | "competitorPos" | "difficulty";

const STATUS_META: Record<OverlapRow["status"], { label: string; dot: string; row: string }> = {
  winning:          { label: "Winning",  dot: "bg-emerald-500", row: "" },
  losing:           { label: "Losing",   dot: "bg-red-500",     row: "bg-red-50/30" },
  brand_only:       { label: "Brand only", dot: "bg-blue-500",  row: "" },
  competitor_only:  { label: "Gap",      dot: "bg-slate-400",   row: "bg-amber-50/30" },
  unknown:          { label: "—",        dot: "bg-slate-300",   row: "" },
};

export default function KeywordBattles({
  brandId, brandDomain, competitors, contentGapKeywords = [], onTrackKeyword, onCreateBrief, onCreateLlmoBrief,
}: Props) {
  const [selectedId, setSelectedId] = useState<string>(competitors[0]?.id ?? "");
  const [data, setData] = useState<OverlapResponse | null>(null);
  const [loading, setLoading] = useState(false);
  const [search, setSearch] = useState("");
  const [statusFilter, setStatusFilter] = useState<StatusFilter>("all");
  const [sortKey, setSortKey] = useState<SortKey>("volume");
  // Keyword of the currently-expanded row, or null when every row
  // is collapsed. Accordion — clicking a row opens it + closes any
  // other. Expanded body shows SERP features + landing URLs + the
  // raw volume/CPC/difficulty numbers (sometimes hidden in a
  // scroll-overflowed cell on narrow viewports).
  const [expandedKeyword, setExpandedKeyword] = useState<string | null>(null);

  // Per-competitor result cache. Switching competitors should be free
  // after the first fetch — only the first hit costs a SERP-provider call.
  const [cache, setCache] = useState<Record<string, OverlapResponse>>({});

  const load = useCallback(async (competitorId: string, force = false) => {
    if (!competitorId) return;
    if (cache[competitorId] && !force) {
      setData(cache[competitorId]);
      return;
    }
    setLoading(true);
    try {
      const url = force
        ? `/api/competitors/${competitorId}/keyword-overlap?fresh=1`
        : `/api/competitors/${competitorId}/keyword-overlap`;
      const res = await fetch(url);
      const json = (await res.json()) as OverlapResponse;
      if (!res.ok) {
        // 503 with configured:false is the "SERP provider not set up" path —
        // surface it explicitly instead of just an error string.
        setData({ rows: [], totalCount: 0, costUsd: 0, configured: json.configured ?? true, error: json.error });
      } else {
        setData(json);
        setCache((prev) => ({ ...prev, [competitorId]: json }));
      }
    } catch (err) {
      setData({ rows: [], totalCount: 0, costUsd: 0, error: err instanceof Error ? err.message : "Network error" });
    } finally {
      setLoading(false);
    }
  }, [cache]);

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

  // Re-pick competitor when the list changes (e.g., on add/delete).
  useEffect(() => {
    if (!competitors.find((c) => c.id === selectedId) && competitors[0]) {
      setSelectedId(competitors[0].id);
    }
  }, [competitors, selectedId]);

  const visibleRows = useMemo(() => {
    // When the "Content Gaps" filter is active, show keywords
    // extracted from the Content Analysis gap pages instead of the
    // SERP-provider battles data. Each content gap becomes a row with
    // status "competitor_only" since the brand has no page for it.
    if (statusFilter === "content_gap") {
      const q = search.toLowerCase();
      return contentGapKeywords
        .filter((g) => !q || g.keyword.includes(q))
        .sort((a, b) => b.confidence - a.confidence)
        .slice(0, 100)
        .map((g): OverlapRow => ({
          keyword: g.keyword,
          volume: null,
          cpc: null,
          difficulty: null,
          brandPosition: null,
          brandUrl: null,
          competitorPosition: null,
          competitorUrl: g.competitorUrl,
          status: "competitor_only",
          serpFeatures: [],
        }));
    }

    if (!data?.rows) return [];
    const q = search.toLowerCase();
    let rows = data.rows.filter((r) => {
      if (statusFilter === "all" && r.status === "brand_only") return false;
      if (statusFilter !== "all" && r.status !== statusFilter) return false;
      if (q && !r.keyword.toLowerCase().includes(q)) return false;
      return true;
    });
    rows = rows.sort((a, b) => {
      if (sortKey === "diff") {
        const aDiff = (a.competitorPosition ?? 999) - (a.brandPosition ?? 999);
        const bDiff = (b.competitorPosition ?? 999) - (b.brandPosition ?? 999);
        return aDiff - bDiff;
      }
      if (sortKey === "competitorPos") return (a.competitorPosition ?? 999) - (b.competitorPosition ?? 999);
      if (sortKey === "difficulty") return (b.difficulty ?? 0) - (a.difficulty ?? 0);
      return (b.volume ?? 0) - (a.volume ?? 0);
    });
    return rows;
  }, [data, search, statusFilter, sortKey, contentGapKeywords]);

  // ── Empty / config states ────────────────────────────────────────
  if (competitors.length === 0) {
    return (
      <div className="bg-white rounded-xl border border-dashed border-slate-200 p-10 text-center">
        <Swords className="w-8 h-8 text-slate-200 mx-auto mb-2" />
        <p className="text-sm font-semibold text-slate-600">Add a competitor to see keyword battles</p>
        <p className="text-xs text-slate-400 mt-1">Live SERP data — keywords you both rank for, side by side.</p>
      </div>
    );
  }

  const selected = competitors.find((c) => c.id === selectedId);

  // Battle Score — wins vs shared. "Shared" means both domains have
  // a position on the keyword (brandPosition AND competitorPosition
  // non-null). "Win" means brand position < competitor position
  // (lower is better). Ties aren't counted as wins. Excludes
  // competitor_only and brand_only rows so the denominator reflects
  // actual head-to-head matchups only.
  const battleScore = useMemo(() => {
    if (!data?.rows) return null;
    let wins = 0;
    let shared = 0;
    for (const r of data.rows) {
      if (r.brandPosition != null && r.competitorPosition != null) {
        shared++;
        if (r.brandPosition < r.competitorPosition) wins++;
      }
    }
    if (shared === 0) return null;
    return { wins, shared, pct: Math.round((wins / shared) * 100) };
  }, [data]);

  const aiIndicatorItems = useMemo(() => {
    if (!data?.rows) return [];
    return data.rows.map((r) => ({ type: "keyword" as const, value: r.keyword }));
  }, [data]);
  const aiData = useAiIndicators(brandId, aiIndicatorItems);

  const aiBattleSummary = useMemo(() => {
    if (aiData.loading || !data?.rows) return null;
    let brandWins = 0, competitorWins = 0, bothCited = 0, neitherCited = 0;
    for (const row of data.rows) {
      const d = aiData.keywords[row.keyword];
      const brandCited = d?.cited ?? false;
      if (brandCited && row.status === "winning") brandWins++;
      else if (!brandCited && row.status === "losing") competitorWins++;
      else if (brandCited) bothCited++;
      else neitherCited++;
    }
    if (brandWins === 0 && competitorWins === 0) return null;
    return { brandWins, competitorWins, bothCited, neitherCited };
  }, [aiData, data]);

  return (
    <div className="bg-white rounded-xl border border-slate-200 overflow-hidden" data-brand-id={brandId}>
      {/* Header */}
      <div className="px-5 py-3.5 border-b border-slate-100 flex items-center justify-between gap-4 flex-wrap">
        <div className="flex items-center gap-2 flex-wrap">
          <Swords className="w-4 h-4 text-rose-600" />
          <h3 className="font-bold text-slate-800 text-sm">Keyword Battles</h3>
          {data && data.totalCount > 0 && (
            <span className="text-[10px] font-semibold text-slate-400">
              {data.totalCount} shared keywords with {selected?.domain ?? ""}
            </span>
          )}
          {/* Battle Score — headline summary of the head-to-head
              matchup. Green when brand wins majority, amber when even,
              rose when brand is losing. Reads as a single glance metric
              so users don't have to count wins in the table. */}
          {battleScore && (
            <span
              className={`inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-2 py-0.5 rounded-full border ${
                battleScore.pct >= 60 ? "bg-emerald-50 text-emerald-700 border-emerald-200"
                : battleScore.pct >= 40 ? "bg-amber-50 text-amber-700 border-amber-200"
                : "bg-rose-50 text-rose-700 border-rose-200"
              }`}
              title={`You outrank the competitor on ${battleScore.wins} of ${battleScore.shared} shared head-to-head keywords.`}
            >
              You win {battleScore.wins}/{battleScore.shared} · {battleScore.pct}%
            </span>
          )}
          {/* AI Battle Summary */}
          {aiBattleSummary && (
            <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-2 py-0.5 rounded-full border bg-violet-50 text-violet-700 border-violet-200">
              <Bot className="w-2.5 h-2.5" />
              AI: You {aiBattleSummary.brandWins} · Them {aiBattleSummary.competitorWins}
            </span>
          )}
          {/* Source badge — only renders for the AI-estimate fallback
              so the user knows positions are approximate. Live SERP
              rows don't need a badge; they're the default shape. */}
          {data?.source === "ai_estimate" && (
            <span className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border bg-violet-50 text-violet-700 border-violet-200">
              AI estimate
            </span>
          )}
          {data?.source === "ai_estimate_enriched" && (
            <span
              className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border bg-emerald-50 text-emerald-700 border-emerald-200"
              title={`${data.enrichedCount ?? 0} rows verified from GSC or Rank Tracker. Competitor positions still AI-estimated.`}
            >
              Real data {data.enrichedCount ? `· ${data.enrichedCount} verified` : ""}
            </span>
          )}
          {/* Cached badge — signals that the rows are reused from the
              7-day server cache. Gives users confidence they can tap
              Refresh to force a fresh SERP-provider call when they know
              something's changed. */}
          {data?.cached && (
            <span
              className="inline-flex items-center gap-1 text-[10px] font-bold uppercase tracking-wide px-1.5 py-0.5 rounded border bg-slate-50 text-slate-600 border-slate-200"
              title={`Cached ${data.cachedAgeHours ?? "?"}h ago. Hit Refresh to fetch live data.`}
            >
              Cached{typeof data.cachedAgeHours === "number" ? ` · ${data.cachedAgeHours}h` : ""}
            </span>
          )}
        </div>
        <div className="flex items-center gap-2">
          {/* Refresh button only offered when we have data — bypasses
              the 7-day cache via ?fresh=1 on the next load. Disabled
              while loading so users can't double-fire SERP-provider
              calls. */}
          {data && data.source !== "none" && (
            <button
              onClick={() => {
                // Evict the client-side cache for this competitor too,
                // otherwise load() would return the stale in-memory
                // copy before hitting ?fresh=1.
                setCache((prev) => {
                  const next = { ...prev };
                  delete next[selectedId];
                  return next;
                });
                load(selectedId, true);
              }}
              disabled={loading}
              className="text-[10px] font-semibold text-slate-500 hover:text-slate-700 underline underline-offset-2 disabled:opacity-40"
              title="Force a fresh SERP fetch, bypassing the 7-day server cache"
            >
              Refresh
            </button>
          )}
          <select
            value={selectedId}
            onChange={(e) => setSelectedId(e.target.value)}
            className="text-[11px] border border-slate-200 rounded-lg px-2.5 py-1.5 bg-white"
          >
            {competitors.map((c) => (
              <option key={c.id} value={c.id}>{c.domain}</option>
            ))}
          </select>
        </div>
      </div>

      {/* Filter row */}
      <div className="px-5 py-2.5 border-b border-slate-100 flex items-center gap-2 flex-wrap">
        <div className="relative">
          <Search className="w-3 h-3 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2" />
          <input
            value={search}
            onChange={(e) => setSearch(e.target.value)}
            placeholder="Search keywords…"
            className="pl-7 pr-3 py-1.5 text-[11px] border border-slate-200 rounded-lg w-48 focus:outline-none focus:ring-2 focus:ring-brand-500/40"
          />
        </div>
        <div className="flex items-center gap-1">
          <Filter className="w-3 h-3 text-slate-400" />
          {(["all", "losing", "winning", "competitor_only", ...(contentGapKeywords.length > 0 ? ["content_gap" as StatusFilter] : [])] as StatusFilter[]).map((s) => {
            const label = s === "all" ? "All"
              : s === "competitor_only" ? "Gaps"
              : s === "content_gap" ? `Content Gaps (${contentGapKeywords.length})`
              : s.charAt(0).toUpperCase() + s.slice(1);
            return (
              <button
                key={s}
                onClick={() => setStatusFilter(s)}
                className={`px-2.5 py-1 rounded-lg text-[10px] font-semibold uppercase tracking-wide ${
                  statusFilter === s
                    ? s === "content_gap" ? "bg-violet-50 text-violet-700 border border-violet-200" : "bg-rose-50 text-rose-700 border border-rose-200"
                    : "text-slate-500 hover:bg-slate-100 border border-transparent"
                }`}
              >
                {label}
              </button>
            );
          })}
        </div>
        <div className="ml-auto flex items-center gap-2 text-[10px] text-slate-400">
          <span>Sort:</span>
          <select
            value={sortKey}
            onChange={(e) => setSortKey(e.target.value as SortKey)}
            className="text-[11px] border border-slate-200 rounded-lg px-2 py-1 bg-white"
          >
            <option value="volume">Volume</option>
            <option value="diff">Position diff</option>
            <option value="competitorPos">Their position</option>
            <option value="difficulty">Difficulty</option>
          </select>
        </div>
      </div>

      {/* Body */}
      {loading ? (
        <div className="p-12 flex items-center justify-center text-slate-400">
          <Loader2 className="w-5 h-5 animate-spin mr-2" /> Loading keyword overlap…
        </div>
      ) : data?.configured === false ? (
        <div className="p-10 text-center">
          <AlertCircle className="w-6 h-6 text-amber-500 mx-auto mb-2" />
          <p className="text-sm font-semibold text-slate-600">Live keyword overlap data unavailable</p>
          <p className="text-xs text-slate-400 mt-1">
            The SERP data provider isn&apos;t configured for this workspace yet. Contact support to enable.
          </p>
        </div>
      ) : visibleRows.length === 0 ? (
        <div className="p-10 text-center text-xs text-slate-400">
          {data?.rows.length === 0
            ? (data?.note ?? "No shared ranking keywords found. Try a different competitor or location.")
            : "No keywords match your filters."}
        </div>
      ) : (
        <div className="overflow-x-auto">
          {/* AI-estimate source note — rendered above the table so
              users don't miss the "these are approximate" disclaimer
              when the fallback kicks in. */}
          {data?.source === "ai_estimate" && data?.note && (
            <div className="px-5 py-2.5 bg-violet-50 border-b border-violet-100 flex items-start gap-2">
              <AlertCircle className="w-3.5 h-3.5 text-violet-600 mt-0.5 shrink-0" />
              <p className="text-[11px] text-violet-800">{data.note}</p>
            </div>
          )}
          <table className="w-full text-sm">
            <thead>
              <tr className="text-left text-[11px] font-semibold uppercase tracking-wider text-slate-400 border-b border-slate-100 bg-slate-50">
                <th className="px-5 py-2.5">Keyword</th>
                <th className="px-5 py-2.5 text-right">Vol</th>
                <th className="px-5 py-2.5 text-center">You</th>
                <th className="px-5 py-2.5 text-center">Them</th>
                <th className="px-5 py-2.5 text-right">Diff</th>
                <th className="px-5 py-2.5 text-center">Status</th>
                <th className="px-5 py-2.5 text-center">AI</th>
                <th className="px-5 py-2.5 text-right">Action</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-100">
              {visibleRows.slice(0, 100).map((row) => {
                const meta = STATUS_META[row.status];
                const isExpanded = expandedKeyword === row.keyword;
                // Whether this row has ANY SERP-detail content worth
                // expanding for. Without this guard, clicking a row
                // with no SERP features + no URLs would produce an
                // empty expanded panel.
                const hasExpandContent = row.serpFeatures.length > 0 || row.competitorUrl || (row.cpc != null) || (row.difficulty != null);
                return (
                  <Fragment key={row.keyword}>
                  <tr
                    className={`hover:bg-slate-50 transition-colors ${meta.row} ${hasExpandContent ? "cursor-pointer" : ""} ${isExpanded ? "bg-slate-50/80" : ""}`}
                    onClick={hasExpandContent ? () => setExpandedKeyword((cur) => cur === row.keyword ? null : row.keyword) : undefined}
                  >
                    <td className="px-5 py-2.5">
                      <p className="text-xs font-semibold text-slate-800">{row.keyword}</p>
                      {row.brandUrl && (
                        <a
                          href={`https://${brandDomain.replace(/^https?:\/\//, "")}${row.brandUrl}`}
                          target="_blank"
                          rel="noopener noreferrer"
                          onClick={(e) => e.stopPropagation()}
                          className="text-[10px] text-brand-600 hover:underline inline-flex items-center gap-1"
                          title={row.brandUrl}
                        >
                          {row.brandUrl.slice(0, 60)} <ExternalLink className="w-2.5 h-2.5" />
                        </a>
                      )}
                    </td>
                    <td className="px-5 py-2.5 text-right text-xs text-slate-700 tabular-nums">
                      {row.volume?.toLocaleString() ?? "—"}
                    </td>
                    <td className="px-5 py-2.5 text-center text-xs font-bold tabular-nums">
                      {/* Brand position is "real" whenever the source
                          is "live" (DataForSEO) or "ai_estimate_enriched"
                          (GSC/Rank Tracker verified). Only raw AI
                          estimates get the "~" approximate marker. */}
                      {row.brandPosition != null
                        ? `${data?.source === "ai_estimate" ? "~" : ""}#${row.brandPosition}`
                        : <span className="text-slate-300">—</span>}
                    </td>
                    <td className="px-5 py-2.5 text-center text-xs font-bold tabular-nums">
                      {row.competitorPosition != null
                        ? `${data?.source === "live" ? "" : "~"}#${row.competitorPosition}`
                        : <span className="text-slate-300">—</span>}
                    </td>
                    <td className="px-5 py-2.5 text-right text-[11px] tabular-nums">
                      {row.brandPosition != null && row.competitorPosition != null ? (
                        <span className={row.brandPosition < row.competitorPosition ? "text-emerald-600" : "text-red-500"}>
                          {row.brandPosition < row.competitorPosition ? "+" : ""}
                          {row.competitorPosition - row.brandPosition}
                        </span>
                      ) : "—"}
                    </td>
                    <td className="px-5 py-2.5 text-center">
                      <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-slate-600">
                        <span className={`w-1.5 h-1.5 rounded-full ${meta.dot}`} />
                        {meta.label}
                      </span>
                    </td>
                    <td className="px-5 py-2.5 text-center">
                      {(() => {
                        const d = aiData.keywords[row.keyword];
                        if (!d) return <span className="text-slate-300 text-[10px]">—</span>;
                        if (d.cited && row.status === "winning") return <AiBadge type="cited" size="sm" tooltip="You're cited and winning" />;
                        if (d.cited) return <AiBadge type="cited" size="sm" />;
                        if (d.opportunity) return <AiBadge type="opportunity" size="sm" />;
                        return <span className="text-slate-300 text-[10px]">—</span>;
                      })()}
                    </td>
                    <td className="px-5 py-2.5 text-right">
                      {row.status === "competitor_only" && (onCreateBrief || onCreateLlmoBrief) ? (
                        <span className="inline-flex items-center gap-1">
                          {onCreateBrief && (
                            <button
                              onClick={(e) => { e.stopPropagation(); onCreateBrief(row.keyword); }}
                              className="inline-flex items-center gap-1 text-[10px] font-semibold px-2 py-1 rounded-lg bg-amber-50 text-amber-700 border border-amber-200 hover:bg-amber-100"
                              title="Create SEO Content Brief"
                            >
                              <FileText className="w-3 h-3" /> SEO
                            </button>
                          )}
                          {onCreateLlmoBrief && (
                            <button
                              onClick={(e) => { e.stopPropagation(); onCreateLlmoBrief(row.keyword, selected?.domain); }}
                              className="inline-flex items-center gap-1 text-[10px] font-semibold px-2 py-1 rounded-lg bg-violet-50 text-violet-700 border border-violet-200 hover:bg-violet-100"
                              title="Create LLMO Content Brief (AI-optimized)"
                            >
                              <Bot className="w-3 h-3" /> LLMO
                            </button>
                          )}
                        </span>
                      ) : onTrackKeyword ? (
                        <button
                          onClick={(e) => { e.stopPropagation(); onTrackKeyword(row.keyword); }}
                          className="inline-flex items-center gap-1 text-[10px] font-semibold px-2 py-1 rounded-lg bg-brand-50 text-brand-700 border border-brand-200 hover:bg-brand-100"
                        >
                          <Plus className="w-3 h-3" /> Track
                        </button>
                      ) : (
                        <ArrowUpRight className="w-3 h-3 text-slate-300 inline" />
                      )}
                    </td>
                  </tr>
                  {/* Expanded SERP details row — shown when the user
                      clicks a row with any expandable content. Covers
                      SERP features (featured snippets, site links,
                      PAA boxes, etc.), competitor landing URL, and
                      the CPC / difficulty numbers that would otherwise
                      be hidden on narrow viewports. Accordion: only
                      one row open at a time. */}
                  {isExpanded && (
                    <tr className="bg-slate-50/60">
                      <td colSpan={7} className="px-5 py-3 border-t border-slate-100">
                        <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs">
                          {/* Brand URL (repeated here for alignment
                              with the competitor URL column — inside
                              the table body the brand URL is truncated
                              at 60 chars with ellipsis). */}
                          <div>
                            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-1">Your landing URL</p>
                            {row.brandUrl ? (
                              <a
                                href={`https://${brandDomain.replace(/^https?:\/\//, "")}${row.brandUrl}`}
                                target="_blank"
                                rel="noopener noreferrer"
                                className="text-[11px] font-mono text-brand-600 hover:underline break-all"
                              >
                                {row.brandUrl}
                              </a>
                            ) : <p className="text-[11px] text-slate-400 italic">Not ranking</p>}
                          </div>
                          <div>
                            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-1">Competitor landing URL</p>
                            {row.competitorUrl ? (
                              <a
                                href={row.competitorUrl}
                                target="_blank"
                                rel="noopener noreferrer"
                                className="text-[11px] font-mono text-slate-700 hover:text-slate-900 hover:underline break-all"
                              >
                                {row.competitorUrl}
                              </a>
                            ) : <p className="text-[11px] text-slate-400 italic">Not ranking</p>}
                          </div>
                          <div>
                            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-1">Keyword metrics</p>
                            <ul className="space-y-0.5 text-[11px] text-slate-700">
                              <li><span className="text-slate-400">Volume:</span> <span className="font-semibold tabular-nums">{row.volume != null ? row.volume.toLocaleString() : "—"}</span></li>
                              <li><span className="text-slate-400">CPC:</span> <span className="font-semibold tabular-nums">{row.cpc != null ? `$${row.cpc.toFixed(2)}` : "—"}</span></li>
                              <li><span className="text-slate-400">Difficulty:</span> <span className="font-semibold tabular-nums">{row.difficulty != null ? `${row.difficulty}/100` : "—"}</span></li>
                            </ul>
                          </div>
                        </div>
                        {row.serpFeatures.length > 0 && (
                          <div className="mt-3 pt-3 border-t border-slate-100">
                            <p className="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-1.5">SERP features present</p>
                            <div className="flex flex-wrap gap-1.5">
                              {row.serpFeatures.map((feat) => (
                                <span key={feat} className="text-[10px] font-semibold px-2 py-0.5 rounded border bg-white border-slate-200 text-slate-700">
                                  {feat.replace(/_/g, " ")}
                                </span>
                              ))}
                            </div>
                          </div>
                        )}
                      </td>
                    </tr>
                  )}
                  </Fragment>
                );
              })}
            </tbody>
          </table>
          {data && visibleRows.length > 100 && (
            <p className="px-5 py-2 text-[10px] text-slate-400 text-center border-t border-slate-100">
              Showing top 100 of {visibleRows.length} matching keywords. Refine filters to narrow.
            </p>
          )}
        </div>
      )}
    </div>
  );
}
