"use client";

import { useState, useEffect, useCallback } from "react";
import { useSearchParams } from "next/navigation";
import { useBrand } from "@/context/brand-context";
import GbpLocationPicker from "@/components/integrations/gbp-location-picker";
import {
  Plug,
  CheckCircle2,
  XCircle,
  Clock,
  Search,
  MapPin,
  Bot,
  Users,
  ArrowRight,
  Sparkles,
  BarChart3,
  Phone,
  Star,
  ShoppingCart,
  Database,
  Globe,
  TrendingUp,
  RefreshCw,
  RotateCw,
  AlertCircle,
  AlertTriangle,
  Megaphone,
  Tv,
  Headphones,
  Mail,
} from "lucide-react";

// ─── Types ────────────────────────────────────────────────────────────────────

type Section = "recommended" | "seo" | "ai" | "crm" | "social" | "call" | "local" | "ecommerce" | "data" | "paid_media" | "ott_ctv" | "audio" | "email_sms";
type FilterKey = "all" | Section | "connected" | "not-connected" | "coming-soon";

interface Integration {
  id:          string;
  name:        string;
  description: string;
  connected:   boolean;
  comingSoon?: boolean;
  lastSynced?: string;
  status?:     string;           // "idle"|"connected"|"syncing"|"error"|"expired"
  syncError?:  string;
  tokenExpired?: boolean;
  syncRetryCount?: number;
  nextRetryAt?: string;
  healthMessage?: string;
  sections:    Section[];
}

// Integrations that have sync services on the backend
const SYNCABLE_IDS = ["gsc", "ga4", "gbp", "pagespeed", "bing", "claude"];

interface SectionMeta {
  label: string;
  icon:  React.ComponentType<{ className?: string }>;
  accent: string;
  bg:     string;
}

// ─── Section config ──────────────────────────────────────────────────────────

const SECTION_META: Record<Section, SectionMeta> = {
  recommended: { label: "Recommended Core Integrations", icon: Sparkles,      accent: "text-brand-600",  bg: "bg-brand-50" },
  seo:         { label: "SEO / Organic",                 icon: Search,        accent: "text-blue-600",   bg: "bg-blue-50" },
  ai:          { label: "AI Programs",                   icon: Bot,           accent: "text-violet-600", bg: "bg-violet-50" },
  crm:         { label: "CRM",                           icon: Users,         accent: "text-green-600",  bg: "bg-green-50" },
  social:      { label: "Social",                        icon: Globe,         accent: "text-pink-600",   bg: "bg-pink-50" },
  call:        { label: "Call Tracking",                 icon: Phone,         accent: "text-amber-600",  bg: "bg-amber-50" },
  local:       { label: "Local / Reputation",            icon: Star,          accent: "text-orange-600", bg: "bg-orange-50" },
  ecommerce:   { label: "Ecommerce",                    icon: ShoppingCart,   accent: "text-emerald-600", bg: "bg-emerald-50" },
  data:        { label: "Data / Reporting",              icon: Database,      accent: "text-sky-600",    bg: "bg-sky-50" },
  paid_media:  { label: "Paid Media",                   icon: Megaphone,     accent: "text-red-600",    bg: "bg-red-50" },
  ott_ctv:     { label: "OTT / CTV",                    icon: Tv,            accent: "text-indigo-600", bg: "bg-indigo-50" },
  audio:       { label: "Audio",                         icon: Headphones,    accent: "text-fuchsia-600", bg: "bg-fuchsia-50" },
  email_sms:   { label: "Email / SMS",                   icon: Mail,          accent: "text-teal-600",   bg: "bg-teal-50" },
};

const SECTION_ORDER: Section[] = ["recommended", "seo", "paid_media", "ott_ctv", "audio", "email_sms", "ai", "crm", "social", "call", "local", "ecommerce", "data"];

// ─── Filter buttons ──────────────────────────────────────────────────────────

const FILTERS: { key: FilterKey; label: string }[] = [
  { key: "all",            label: "All" },
  { key: "recommended",    label: "Recommended" },
  { key: "seo",            label: "SEO / Organic" },
  { key: "ai",             label: "AI Programs" },
  { key: "crm",            label: "CRM" },
  { key: "social",         label: "Social" },
  { key: "call",           label: "Call Tracking" },
  { key: "local",          label: "Local / Reputation" },
  { key: "ecommerce",      label: "Ecommerce" },
  { key: "data",           label: "Data / Reporting" },
  { key: "paid_media",     label: "Paid Media" },
  { key: "ott_ctv",        label: "OTT / CTV" },
  { key: "audio",          label: "Audio" },
  { key: "email_sms",      label: "Email / SMS" },
  { key: "connected",      label: "Connected" },
  { key: "not-connected",  label: "Not Connected" },
  { key: "coming-soon",    label: "Coming Soon" },
];

// ─── Mock data ────────────────────────────────────────────────────────────────

const INTEGRATIONS: Integration[] = [
  // ── Active integrations (real OAuth flows) ──
  { id: "gsc",           name: "Google Search Console",   description: "Impressions, clicks, CTR, and keyword positions.",        connected: false, sections: ["recommended", "seo"] },
  { id: "ga4",           name: "Google Analytics 4",      description: "Traffic, sessions, users, and conversion events.",        connected: false, sections: ["recommended"] },
  { id: "gbp",           name: "Google Business Profile", description: "Local visibility, reviews, and map pack insights.",      connected: false, comingSoon: true, sections: ["recommended", "local"] },

  // ── Coming Soon — SEO Tools ──
  { id: "semrush",       name: "Semrush",                 description: "Keyword research, competitive analysis, and audits.",    connected: false, comingSoon: true, sections: ["seo"] },
  { id: "ahrefs",        name: "Ahrefs",                  description: "Backlink analysis, keyword research, and rank tracking.", connected: false, comingSoon: true, sections: ["seo"] },
  { id: "bing",          name: "Bing Webmaster Tools",    description: "Bing search performance, crawl data, and submissions.", connected: false, comingSoon: true, sections: ["seo"] },
  { id: "moz",           name: "Moz",                     description: "Domain authority, keyword tracking, and link analysis.", connected: false, comingSoon: true, sections: ["seo"] },

  // ── Coming Soon — AI Platforms ──
  { id: "chatgpt",       name: "ChatGPT",                 description: "AI visibility monitoring and citation tracking.",        connected: false, comingSoon: true, sections: ["ai"] },
  { id: "perplexity",    name: "Perplexity",              description: "AI search citation monitoring and brand mentions.",      connected: false, comingSoon: true, sections: ["ai"] },
  { id: "gemini",        name: "Gemini",                  description: "Google AI visibility and citation monitoring.",           connected: false, comingSoon: true, sections: ["ai"] },
  { id: "claude",        name: "Claude",                  description: "AI visibility scans and SEO recommendation generation.", connected: false, comingSoon: true, sections: ["ai"] },

  // ── Coming Soon — CRM ──
  { id: "hubspot",       name: "HubSpot",                 description: "CRM contacts, deals, and marketing attribution.",        connected: false, comingSoon: true, sections: ["crm"] },
  { id: "salesforce",    name: "Salesforce",              description: "Enterprise CRM with lead tracking and revenue data.",    connected: false, comingSoon: true, sections: ["crm"] },
  { id: "highlevel",     name: "HighLevel",               description: "All-in-one CRM, funnels, and marketing automation.",     connected: false, comingSoon: true, sections: ["crm"] },

  // ── Coming Soon — Call Tracking ──
  { id: "callrail",      name: "CallRail",                description: "Call tracking, form submissions, and lead attribution.", connected: false, comingSoon: true, sections: ["call"] },
  { id: "ctm",           name: "CallTrackingMetrics",     description: "Call analytics, conversation intelligence.",             connected: false, comingSoon: true, sections: ["call"] },

  // ── Coming Soon — Social ──
  { id: "facebook",      name: "Facebook",                description: "Page insights, ad performance, and audience data.",      connected: false, comingSoon: true, sections: ["social"] },
  { id: "instagram",     name: "Instagram",               description: "Engagement metrics, reach, and content performance.",    connected: false, comingSoon: true, sections: ["social"] },
  { id: "linkedin",      name: "LinkedIn",                description: "Company page analytics and professional engagement.",    connected: false, comingSoon: true, sections: ["social"] },
  { id: "youtube",       name: "YouTube",                 description: "Video analytics, watch time, and channel performance.",  connected: false, comingSoon: true, sections: ["social"] },

  // ── Coming Soon — Ecommerce ──
  { id: "shopify",       name: "Shopify",                 description: "Ecommerce store data, orders, and product performance.", connected: false, comingSoon: true, sections: ["ecommerce"] },
  { id: "stripe",        name: "Stripe",                  description: "Payment processing, revenue tracking.",                  connected: false, comingSoon: true, sections: ["ecommerce"] },

  // ── Coming Soon — Local / Reputation ──
  { id: "birdeye",       name: "Birdeye",                 description: "Reviews, listings, and customer experience management.", connected: false, comingSoon: true, sections: ["local"] },
  { id: "yext",          name: "Yext",                    description: "Listing management, reputation, and local SEO data.",    connected: false, comingSoon: true, sections: ["local"] },
  { id: "yelp",          name: "Yelp",                    description: "Business reviews, ratings, and local search presence.",  connected: false, comingSoon: true, sections: ["local"] },

  // ── Coming Soon — Paid Media ──
  { id: "google_ads",    name: "Google Ads",              description: "Campaign performance, spend, clicks, and conversion verification. Site Tag already captures gclid automatically.",   connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "meta_ads",      name: "Meta Ads",                description: "Facebook and Instagram campaign attribution and ROAS. Site Tag already captures fbclid automatically.",             connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "linkedin_ads",  name: "LinkedIn Ads",            description: "B2B campaign performance and lead attribution. Site Tag already captures li_fat_id automatically.",                connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "tiktok_ads",    name: "TikTok Ads",              description: "Short-form video campaign tracking and conversion attribution. Site Tag already captures ttclid automatically.",   connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "microsoft_ads", name: "Microsoft Ads",           description: "Bing search ads performance and conversion tracking. Site Tag already captures msclkid automatically.",            connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "the_trade_desk",name: "The Trade Desk",          description: "Programmatic display and OTT/CTV campaign attribution.",                                                          connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "dv360",         name: "DV360",                   description: "Google Display & Video 360 programmatic campaign data. Site Tag already captures dclid automatically.",            connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "amazon_dsp",    name: "Amazon DSP",              description: "Amazon programmatic display and video campaign attribution.",                                                      connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "pinterest_ads", name: "Pinterest Ads",           description: "Visual discovery ad performance and conversion tracking.",                                                         connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "snapchat_ads",  name: "Snapchat Ads",            description: "Snapchat campaign performance and audience attribution.",                                                          connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "x_ads",         name: "X (Twitter) Ads",         description: "X/Twitter campaign performance and engagement tracking.",                                                          connected: false, comingSoon: true, sections: ["paid_media"] },
  { id: "reddit_ads",    name: "Reddit Ads",              description: "Reddit campaign performance and community-driven attribution.",                                                    connected: false, comingSoon: true, sections: ["paid_media"] },

  // ── Coming Soon — OTT / CTV ──
  { id: "roku_ads",      name: "Roku Ads",                description: "Streaming TV impression data for cross-device household attribution.",                                             connected: false, comingSoon: true, sections: ["ott_ctv"] },
  { id: "hulu_ads",      name: "Hulu Ad Manager",         description: "Hulu streaming ad impressions and household matching.",                                                            connected: false, comingSoon: true, sections: ["ott_ctv"] },
  { id: "samsung_tv",    name: "Samsung TV Plus",         description: "Samsung connected TV ad impression data.",                                                                         connected: false, comingSoon: true, sections: ["ott_ctv"] },
  { id: "peacock_ads",   name: "Peacock Ads",             description: "NBCUniversal streaming ad impressions and attribution.",                                                           connected: false, comingSoon: true, sections: ["ott_ctv"] },
  { id: "tubi_ads",      name: "Tubi Ads",                description: "Free streaming TV ad impressions and household attribution.",                                                      connected: false, comingSoon: true, sections: ["ott_ctv"] },

  // ── Coming Soon — Audio ──
  { id: "spotify_ads",   name: "Spotify Ad Studio",       description: "Audio ad impressions and listener attribution.",                                                                   connected: false, comingSoon: true, sections: ["audio"] },
  { id: "pandora_ads",   name: "Pandora / SiriusXM",      description: "Audio streaming ad performance data.",                                                                             connected: false, comingSoon: true, sections: ["audio"] },
  { id: "iheart_ads",    name: "iHeart Ads",              description: "Radio and podcast ad impression tracking.",                                                                        connected: false, comingSoon: true, sections: ["audio"] },

  // ── Coming Soon — Email / SMS ──
  { id: "mailchimp",     name: "Mailchimp",               description: "Email campaign performance and subscriber attribution. Site Tag already detects Mailchimp form submissions.",      connected: false, comingSoon: true, sections: ["email_sms"] },
  { id: "klaviyo",       name: "Klaviyo",                 description: "E-commerce email and SMS campaign attribution. Site Tag already detects Klaviyo submissions.",                     connected: false, comingSoon: true, sections: ["email_sms"] },
  { id: "activecampaign",name: "ActiveCampaign",          description: "Email automation and contact attribution. Site Tag already detects ActiveCampaign submissions.",                   connected: false, comingSoon: true, sections: ["email_sms"] },
  { id: "constant_contact",name: "Constant Contact",      description: "Email marketing campaign performance. Site Tag already detects Constant Contact submissions.",                     connected: false, comingSoon: true, sections: ["email_sms"] },
  { id: "convertkit",    name: "ConvertKit",              description: "Creator-focused email marketing attribution. Site Tag already detects ConvertKit submissions.",                    connected: false, comingSoon: true, sections: ["email_sms"] },
  { id: "twilio",        name: "Twilio / SendGrid",       description: "SMS and transactional email delivery data.",                                                                       connected: false, comingSoon: true, sections: ["email_sms"] },
];

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

const CRITICAL_INTEGRATIONS = ["gsc", "ga4"];

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

// Brand ID mapping (frontend mock IDs → database IDs)
export default function IntegrationsPage() {
  const [integrations, setIntegrations] = useState(INTEGRATIONS);
  const [filter, setFilter] = useState<FilterKey>("all");
  const [syncing, setSyncing] = useState<string | null>(null);
  const [forceSyncing, setForceSyncing] = useState(false);
  const [resyncConfirmId, setResyncConfirmId] = useState<string | null>(null);
  const [successMsg, setSuccessMsg] = useState<string | null>(null);
  // Surfaces a /api/integrations fetch failure to the user so the
  // page doesn't silently render "all disconnected" when the
  // backend is down — that state previously looked identical to
  // "you really haven't connected anything", which caused support
  // tickets from users whose GA4 was fine but whose dashboard briefly
  // returned 5xx. Null = loaded OK, string = last error message.
  const [statusLoadError, setStatusLoadError] = useState<string | null>(null);
  const { brand, loading: brandLoading } = useBrand();
  const searchParams = useSearchParams();

  // Property selector state
  const [propertyPickerService, setPropertyPickerService] = useState<string | null>(null);
  const [properties, setProperties] = useState<Array<{ id: string; name: string; account: string; fullId: string }>>([]); 
  const [loadingProperties, setLoadingProperties] = useState(false);
  /** ID of the property currently saved on the brand. The callback
   *  auto-matches a GSC site / GA4 property by domain after OAuth and
   *  pre-saves it here; the picker then highlights that row and
   *  labels it "Current" so the user can one-click confirm or choose
   *  a different option. */
  const [currentPropertyId, setCurrentPropertyId] = useState<string | null>(null);
  const [pickerHandled, setPickerHandled] = useState(false);
  const [pendingConnect, setPendingConnect] = useState<string | null>(null);

  // GBP location picker
  const [gbpPickerOpen, setGbpPickerOpen] = useState(false);
  const [gbpLocationCount, setGbpLocationCount] = useState<number | null>(null);

  // After OAuth callback, show property selector
  // Wait for brand to finish loading before opening the picker
  useEffect(() => {
    // Defensive logging — the post-OAuth picker has had a history of
    // silently not opening on specific brand-context race conditions.
    // These logs make the failure visible in the browser console on
    // the very next reload.
    console.info("[integrations] post-OAuth effect", {
      pickerHandled,
      brandLoading,
      brandId: brand.id,
      hasConnectedParam: !!searchParams.get("connected"),
      connectedParam: searchParams.get("connected"),
      autoMatched: searchParams.get("autoMatched"),
    });

    if (pickerHandled) return;
    if (brandLoading) return;
    if (!brand.id) return;

    const connected = searchParams.get("connected");
    // autoMatched=1 means the callback successfully matched a single
    // GSC site / GA4 property against the brand's domain and pre-saved
    // it so the integration is already usable. We still open the
    // picker — users explicitly want to see the list and confirm the
    // auto-selected property — but we pass autoMatched through so the
    // picker can highlight the matched option.
    const autoMatched = searchParams.get("autoMatched") === "1";
    const error = searchParams.get("error");

    // Guard: if no query params to process, skip
    if (!connected && !error) return;

    // Clear URL params IMMEDIATELY to prevent re-trigger on re-render
    setPickerHandled(true);
    window.history.replaceState({}, "", "/integrations");

    if (connected && ["ga4", "gsc"].includes(connected)) {
      const name = integrations.find(i => i.id === connected)?.name ?? connected;
      setSuccessMsg(
        autoMatched
          ? `${name} connected — we auto-matched a property. Confirm below or pick a different one.`
          : `${name} connected! Select a property below.`,
      );
      setTimeout(() => setSuccessMsg(null), 8000);
      console.info("[integrations] opening property picker for", connected);
      openPropertyPicker(connected);
      return;
    }
    if (connected === "gbp") {
      setSuccessMsg("Google Business Profile connected! API verification typically takes 1-3 business days.");
      setTimeout(() => setSuccessMsg(null), 10000);
      return;
    }
    if (connected) {
      const name = integrations.find(i => i.id === connected)?.name ?? connected;
      setSuccessMsg(`${name} connected successfully!`);
      setTimeout(() => setSuccessMsg(null), 5000);
    }
    if (error) {
      setSuccessMsg(`Connection failed: ${error}`);
      setTimeout(() => setSuccessMsg(null), 5000);
    }
  }, [searchParams, integrations, brandLoading, brand.id, pickerHandled]);

  // Fetch integration status from backend on brand change
  const loadIntegrationStatus = useCallback(async () => {
    if (!brand?.id) return;
    try {
      const res = await fetch(`/api/integrations?brandId=${brand.id}`);
      if (!res.ok) {
        // Non-2xx (usually 500 during a pool hiccup) — surface the
        // failure instead of leaving the page showing "all
        // disconnected" with no explanation. Clears on the next
        // successful load.
        setStatusLoadError(`Could not load integration status (HTTP ${res.status}). Showing last known state.`);
        return;
      }

      const dbIntegrations: Array<{
        integrationId: string; connected: boolean; status: string;
        lastSynced: string | null; syncError: string | null;
        tokenExpired?: boolean; syncRetryCount?: number;
        nextRetryAt?: string | null; healthMessage?: string;
      }> = await res.json();

      setIntegrations(
        INTEGRATIONS.map((item) => {
          const db = dbIntegrations.find((d) => d.integrationId === item.id);
          if (db) {
            return {
              ...item,
              connected: db.connected,
              status: db.status,
              syncError: db.syncError ?? undefined,
              tokenExpired: db.tokenExpired ?? false,
              syncRetryCount: db.syncRetryCount ?? 0,
              nextRetryAt: db.nextRetryAt ?? undefined,
              healthMessage: db.healthMessage ?? undefined,
              lastSynced: db.lastSynced
                ? new Date(db.lastSynced).toLocaleString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" })
                : undefined,
            };
          }
          return { ...item, connected: false, lastSynced: undefined };
        })
      );
      // Successful load — clear any stale error banner from a prior
      // fetch failure so the user sees an unobstructed view of the
      // real integration state.
      setStatusLoadError(null);
    } catch (err) {
      // Network error / aborted fetch. INTEGRATIONS is the canonical
      // catalog (all `connected: false` until proven otherwise), NOT
      // mock data — the previous comment claiming "fall back to
      // default mock data" was misleading. The real issue is that we
      // were silently swallowing the error, so a transient /api
      // outage looked identical to "nothing is actually connected".
      // Surface it instead; the user can retry from the banner.
      console.warn("[integrations] status load failed", err);
      setStatusLoadError("Could not reach integration status API. Showing last known state.");
    }
  }, [brand.id]);

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

  // Check GBP location count after status loads
  useEffect(() => {
    if (!brand.id) return;
    const gbp = integrations.find((i) => i.id === "gbp");
    if (!gbp?.connected) { setGbpLocationCount(null); return; }
    fetch(`/api/gbp/locations?brandId=${brand.id}`)
      .then((r) => r.ok ? r.json() : null)
      .then((d) => setGbpLocationCount(d?.locations?.length ?? 0))
      .catch(() => setGbpLocationCount(0));
  }, [brand.id, integrations]);

  // GBP picker is MANUAL only — no auto-open
  // Users click "Select Locations" to open it when ready

  // Google OAuth integrations — redirect to Google consent screen instead of POST
  const GOOGLE_OAUTH_IDS = ["gsc", "ga4"];

  function notifyMe(id: string) {
    const item = integrations.find((i) => i.id === id);
    if (!item) return;
    setSuccessMsg(`We'll notify you when ${item.name} integration is available.`);
    setTimeout(() => setSuccessMsg(null), 5000);
    // Persist notification preference (best-effort — ignore failures)
    fetch("/api/integrations/notify-me", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ brandId: brand.id, integrationId: id }),
    }).catch(() => { /* silent */ });
  }

  // ── Nango-managed integrations ─────────────────────────────────────
  // Provider config keys from Nango's dashboard. Integrations in this
  // map show a real "Connect" button instead of "Coming Soon".
  const NANGO_PROVIDERS: Record<string, string> = {
    google_ads: "google-ads",
    meta_ads: "facebook-ads",
    hubspot: "hubspot",
    salesforce: "salesforce",
    callrail: "callrail",
    linkedin_ads: "linkedin-ads",
    mailchimp: "mailchimp",
    klaviyo: "klaviyo",
    shopify: "shopify",
    tiktok_ads: "tiktok-ads",
    microsoft_ads: "microsoft-ads",
    stripe: "stripe",
  };

  const [nangoStatuses, setNangoStatuses] = useState<Record<string, "connected" | "disconnected">>({});
  const [nangoConnecting, setNangoConnecting] = useState<string | null>(null);

  // Fetch Nango connection statuses on brand change
  useEffect(() => {
    if (!brand?.id) return;
    fetch(`/api/integrations/nango-status?brandId=${brand.id}`)
      .then((r) => r.ok ? r.json() : null)
      .then((data) => {
        if (data?.integrations) {
          const map: Record<string, "connected" | "disconnected"> = {};
          for (const [, status] of Object.entries(data.integrations)) {
            // Map Nango labels back to integration IDs
          }
          // Store raw status keyed by integration id
          for (const conn of (data.connections ?? [])) {
            for (const [id, provider] of Object.entries(NANGO_PROVIDERS)) {
              if (conn.provider === provider) map[id] = "connected";
            }
          }
          setNangoStatuses(map);
        }
      })
      .catch(() => null);
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [brand?.id]);

  async function connectNango(integrationId: string) {
    const providerKey = NANGO_PROVIDERS[integrationId];
    if (!providerKey || !brand?.id) return;

    const publicKey = process.env.NEXT_PUBLIC_NANGO_PUBLIC_KEY;
    if (!publicKey) {
      setSuccessMsg("Nango not configured — contact support.");
      setTimeout(() => setSuccessMsg(null), 5000);
      return;
    }

    setNangoConnecting(integrationId);
    try {
      const { default: NangoFrontend } = await import("@nangohq/frontend");
      const nango = new NangoFrontend({ publicKey });
      await nango.auth(providerKey, brand.id);
      setNangoStatuses((prev) => ({ ...prev, [integrationId]: "connected" }));
      const item = integrations.find((i) => i.id === integrationId);
      setSuccessMsg(`${item?.name ?? integrationId} connected successfully!`);
      setTimeout(() => setSuccessMsg(null), 5000);
    } catch (err) {
      setSuccessMsg(`Connection failed: ${err instanceof Error ? err.message : "Unknown error"}`);
      setTimeout(() => setSuccessMsg(null), 5000);
    }
    setNangoConnecting(null);
  }

  async function disconnectNango(integrationId: string) {
    const item = integrations.find((i) => i.id === integrationId);
    if (!brand?.id) return;
    try {
      await fetch("/api/integrations/nango-disconnect", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, provider: NANGO_PROVIDERS[integrationId] ?? integrationId }),
      });
      setNangoStatuses((prev) => ({ ...prev, [integrationId]: "disconnected" }));
      setSuccessMsg(`${item?.name ?? integrationId} disconnected.`);
      setTimeout(() => setSuccessMsg(null), 5000);
    } catch { /* silent */ }
  }

  function isNangoManaged(id: string): boolean {
    return id in NANGO_PROVIDERS;
  }

  function isNangoConnected(id: string): boolean {
    return nangoStatuses[id] === "connected";
  }

  async function toggleConnection(id: string) {
    const item = integrations.find((i) => i.id === id);
    if (!item) return;
    if (item.comingSoon) {
      notifyMe(id);
      return;
    }

    // Expired Google integrations have a dead access_token but still hold a
    // valid record + potentially-valid refresh_token. Clicking "Reconnect"
    // must re-initiate OAuth (which writes fresh tokens into the SAME
    // BrandIntegration row) rather than hitting the disconnect endpoint —
    // disconnect wipes credentials, which is the opposite of what the user
    // wants.
    if (isBroken(item) && GOOGLE_OAUTH_IDS.includes(id)) {
      window.location.href = `/api/auth/google?brandId=${brand.id}&service=${id}`;
      return;
    }

    // For Google integrations that need OAuth: redirect to Google
    if (!item.connected && GOOGLE_OAUTH_IDS.includes(id)) {
      window.location.href = `/api/auth/google?brandId=${brand.id}&service=${id}`;
      return;
    }

    const action = item.connected ? "disconnect" : "connect";
    setSyncing(id);

    // Optimistic update
    setIntegrations((prev) =>
      prev.map((i) =>
        i.id === id ? { ...i, connected: !i.connected, lastSynced: !i.connected ? "Just now" : i.lastSynced } : i
      )
    );

    try {
      const res = await fetch("/api/integrations", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, integrationId: id, action }),
      });

      if (res.ok) {
        // Reload full status from backend to get accurate state
        await loadIntegrationStatus();
      } else {
        // Revert on failure
        setIntegrations((prev) =>
          prev.map((i) => (i.id === id ? { ...i, connected: item.connected } : i))
        );
      }
    } catch {
      setIntegrations((prev) =>
        prev.map((i) => (i.id === id ? { ...i, connected: item.connected } : i))
      );
    }

    setSyncing(null);
  }

  async function syncNow(id: string, force = false) {
    setSyncing(id);
    if (force) setForceSyncing(true);
    setIntegrations((prev) =>
      prev.map((i) => (i.id === id ? { ...i, status: "syncing" } : i))
    );

    try {
      const res = await fetch("/api/integrations", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ brandId: brand.id, integrationId: id, action: "sync", force }),
      });
      const data = await res.json();
      if (res.ok && data.result?.success) {
        setIntegrations((prev) =>
          prev.map((i) =>
            i.id === id ? {
              ...i,
              status: "connected",
              lastSynced: "Just now",
              syncError: undefined,
              tokenExpired: false,
              syncRetryCount: 0,
              healthMessage: data.result?.message,
            } : i
          )
        );
        if (force) {
          setSuccessMsg(data.result?.message ?? "Full re-sync completed.");
          setTimeout(() => setSuccessMsg(null), 5000);
        }
      } else {
        setIntegrations((prev) =>
          prev.map((i) => (i.id === id ? {
            ...i,
            status: data.integration?.status ?? "error",
            syncError: data.result?.message ?? data.error ?? "Sync failed",
            tokenExpired: data.integration?.status === "expired",
          } : i))
        );
      }
    } catch {
      setIntegrations((prev) =>
        prev.map((i) => (i.id === id ? { ...i, status: "error", syncError: "Network error" } : i))
      );
    }
    setSyncing(null);
    setForceSyncing(false);
  }

  function confirmFullResync(id: string) {
    setResyncConfirmId(id);
  }

  async function doFullResync() {
    if (!resyncConfirmId) return;
    const id = resyncConfirmId;
    setResyncConfirmId(null);
    await syncNow(id, true);
  }

  // ── Property picker functions ──
  async function openPropertyPicker(service: string) {
    console.info("[integrations] openPropertyPicker called", { service, brandId: brand.id });
    // Guard: don't call API without a brand ID
    if (!brand.id) {
      console.warn("[integrations] openPropertyPicker bailed — no brand.id");
      setSuccessMsg("No brand selected — select a brand first, then try connecting.");
      setTimeout(() => setSuccessMsg(null), 5000);
      return;
    }

    setPropertyPickerService(service);
    setProperties([]);
    setCurrentPropertyId(null);
    setLoadingProperties(true);
    try {
      const res = await fetch(`/api/integrations/properties?brandId=${encodeURIComponent(brand.id)}&service=${encodeURIComponent(service)}`);
      const data = await res.json();
      console.info("[integrations] property fetch result", {
        service,
        status: res.status,
        count: Array.isArray(data?.properties) ? data.properties.length : null,
        hasCurrentId: !!data?.currentId,
        error: data?.error ?? null,
      });
      if (data.error) {
        setSuccessMsg(`Property loading error: ${data.error}`);
        setTimeout(() => setSuccessMsg(null), 8000);
      }
      setProperties(data.properties ?? []);
      setCurrentPropertyId((data.currentId as string | null) ?? null);
    } catch (err) {
      console.error("[integrations] openPropertyPicker fetch failed", err);
      setSuccessMsg(`Failed to load properties: ${err instanceof Error ? err.message : "unknown"}`);
      setTimeout(() => setSuccessMsg(null), 5000);
    }
    setLoadingProperties(false);
  }

  async function selectProperty(propertyId: string, propertyName: string) {
    if (!propertyPickerService) return;
    const service = propertyPickerService;

    // Close modal immediately
    setPropertyPickerService(null);
    setProperties([]);
    setCurrentPropertyId(null);

    try {
      const saveRes = await fetch("/api/integrations/properties", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          brandId: brand.id,
          service,
          propertyId,
          propertyName,
        }),
      });

      if (!saveRes.ok) {
        const data = await saveRes.json();
        setSuccessMsg(`Failed to save property: ${data.error || "unknown error"}`);
        setTimeout(() => setSuccessMsg(null), 5000);
        return;
      }

      setSuccessMsg(`${propertyName} selected — syncing data...`);

      // Auto-trigger sync after selecting property
      setSyncing(service);
      await fetch("/api/integrations", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          brandId: brand.id,
          integrationId: service,
          action: "sync",
        }),
      });
      await loadIntegrationStatus();
      setSyncing(null);

      setSuccessMsg(`${propertyName} synced successfully!`);
      setTimeout(() => setSuccessMsg(null), 5000);
    } catch (err) {
      setSuccessMsg(`Error: ${err instanceof Error ? err.message : "unknown"}`);
      setSyncing(null);
      setTimeout(() => setSuccessMsg(null), 5000);
    }
  }

  const connectedCount = integrations.filter((i) => i.connected).length;
  const recommendedItems = integrations.filter((i) => i.sections.includes("recommended"));
  const recommendedConnected = recommendedItems.filter((i) => i.connected).length;
  const missingCritical = integrations.filter((i) => CRITICAL_INTEGRATIONS.includes(i.id) && !i.connected);
  const missingCriticalItems = integrations.filter((i) => CRITICAL_INTEGRATIONS.includes(i.id) && !i.connected);

  function isBroken(item: Integration): boolean {
    return item.status === "expired" || (item.status === "error" && !!item.tokenExpired);
  }

  // Filter logic
  function matchesFilter(item: Integration): boolean {
    if (filter === "all") return true;
    if (filter === "connected") return item.connected;
    if (filter === "not-connected") return !item.connected && !item.comingSoon;
    if (filter === "coming-soon") return !!item.comingSoon;
    return item.sections.includes(filter as Section);
  }

  // Get sections to show based on filter
  function getSectionsToShow(): Section[] {
    if (filter === "all" || filter === "connected" || filter === "not-connected" || filter === "coming-soon") return SECTION_ORDER;
    return [filter as Section];
  }

  return (
    <div className="space-y-6">
      {/* ── Header ── */}
      <div>
        <div className="flex items-center gap-3">
          <div className="w-10 h-10 rounded-xl bg-brand-50 flex items-center justify-center shrink-0">
            <Plug className="w-5 h-5 text-brand-600" />
          </div>
          <div>
            <h1 className="text-2xl font-bold text-slate-900">Integrations</h1>
            <p className="text-slate-500 text-sm mt-0.5">
              Connect your tools to enrich SEO data, AI analysis, and reporting.
            </p>
          </div>
          <div className="ml-auto flex items-center gap-2">
            <span className="text-xs font-semibold text-slate-500">{connectedCount} connected</span>
            <span className="text-xs text-slate-300">of {integrations.length}</span>
          </div>
        </div>
      </div>

      {/* ── OAuth success/error message ── */}
      {successMsg && (
        <div className={`rounded-xl px-4 py-3 text-sm font-medium ${successMsg.includes("failed") ? "bg-red-50 text-red-700 border border-red-200" : "bg-green-50 text-green-700 border border-green-200"}`}>
          {successMsg}
        </div>
      )}

      {/* ── Integration-status load error banner ──
          Shown when /api/integrations returned non-2xx or a network
          error took down the fetch. Without this, the user sees the
          page rendering as if nothing is connected — indistinguishable
          from a genuinely empty account. Amber (warning) rather than
          red (error) because the real connection state is still stored
          server-side; we just couldn't read it this render. Retry
          fires loadIntegrationStatus() again. */}
      {statusLoadError && (
        <div className="rounded-xl px-4 py-3 text-sm bg-amber-50 text-amber-800 border border-amber-200 flex items-start justify-between gap-3">
          <span>{statusLoadError}</span>
          <button
            onClick={() => loadIntegrationStatus()}
            className="text-xs font-semibold underline shrink-0"
          >
            Retry
          </button>
        </div>
      )}

      {/* ── Full Re-sync Confirmation Modal ── */}
      {resyncConfirmId && (
        <>
          <div className="fixed inset-0 bg-black/30 z-40" onClick={() => setResyncConfirmId(null)} />
          <div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white rounded-2xl border border-slate-200 shadow-2xl w-full max-w-md overflow-hidden">
            <div className="px-6 py-5">
              <div className="flex items-start gap-3 mb-3">
                <div className="w-10 h-10 rounded-xl bg-amber-50 border border-amber-200 flex items-center justify-center shrink-0">
                  <AlertTriangle className="w-5 h-5 text-amber-600" />
                </div>
                <div>
                  <h2 className="text-base font-bold text-slate-900">Full Re-sync?</h2>
                  <p className="text-xs text-slate-500 mt-0.5">This will re-download 90 days of search data.</p>
                </div>
              </div>
              <p className="text-xs text-slate-600 leading-relaxed mb-4">
                Existing cached snapshots, queries, and pages for this brand will be deleted and replaced with a fresh 90-day backfill from Google Search Console. This typically takes 10–30 seconds.
              </p>
              <div className="flex items-center justify-end gap-2">
                <button
                  onClick={() => setResyncConfirmId(null)}
                  className="px-4 py-2 text-xs font-semibold text-slate-600 hover:bg-slate-50 rounded-lg transition-colors"
                >
                  Cancel
                </button>
                <button
                  onClick={doFullResync}
                  className="flex items-center gap-1.5 px-4 py-2 text-xs font-semibold text-white bg-brand-600 hover:bg-brand-700 rounded-lg transition-colors"
                >
                  <RotateCw className="w-3 h-3" />
                  Re-sync 90 Days
                </button>
              </div>
            </div>
          </div>
        </>
      )}

      {/* ── Property Picker Modal ── */}
      {propertyPickerService && (
        <>
          <div className="fixed inset-0 bg-black/30 z-40" onClick={() => { setPropertyPickerService(null); setProperties([]); setCurrentPropertyId(null); }} />
          <div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white rounded-2xl border border-slate-200 shadow-2xl w-full max-w-md overflow-hidden">
            <div className="px-6 py-4 border-b border-slate-100">
              <h2 className="text-lg font-bold text-slate-900">
                Select {propertyPickerService === "ga4" ? "GA4 Property" : propertyPickerService === "gsc" ? "Search Console Site" : "Property"}
              </h2>
              <p className="text-xs text-slate-500 mt-0.5">
                Choose which {propertyPickerService === "ga4" ? "Google Analytics property" : "site"} to connect to this brand.
              </p>
            </div>

            <div className="max-h-80 overflow-y-auto">
              {loadingProperties ? (
                <div className="px-6 py-8 text-center">
                  <RefreshCw className="w-5 h-5 text-brand-500 animate-spin mx-auto mb-2" />
                  <p className="text-sm text-slate-500">Loading properties from Google...</p>
                </div>
              ) : properties.length === 0 ? (
                <div className="px-6 py-8 text-center">
                  <p className="text-sm text-slate-500">No properties found.</p>
                  <p className="text-xs text-slate-400 mt-1">Make sure your Google account has access to at least one {propertyPickerService === "ga4" ? "GA4 property" : "site"}.</p>
                </div>
              ) : (
                <div className="divide-y divide-slate-100">
                  {properties.map((prop) => {
                    const isCurrent = currentPropertyId != null && prop.id === currentPropertyId;
                    return (
                      <button
                        key={prop.id}
                        onClick={() => selectProperty(prop.id, prop.name)}
                        className={`w-full px-6 py-3.5 flex items-center gap-3 transition-colors text-left ${
                          isCurrent ? "bg-brand-50 hover:bg-brand-100" : "hover:bg-brand-50"
                        }`}
                      >
                        <div className={`w-8 h-8 rounded-lg flex items-center justify-center shrink-0 ${
                          isCurrent ? "bg-brand-100" : "bg-blue-50"
                        }`}>
                          <BarChart3 className={`w-4 h-4 ${isCurrent ? "text-brand-600" : "text-blue-600"}`} />
                        </div>
                        <div className="flex-1 min-w-0">
                          <div className="flex items-center gap-2">
                            <p className="text-sm font-semibold text-slate-800 truncate">{prop.name}</p>
                            {isCurrent && (
                              <span className="shrink-0 text-[10px] font-bold uppercase tracking-wide text-brand-700 bg-brand-100 border border-brand-200 rounded px-1.5 py-0.5">
                                Current
                              </span>
                            )}
                          </div>
                          <p className="text-[11px] text-slate-400">{prop.account} · ID: {prop.id}</p>
                        </div>
                        <ArrowRight className="w-4 h-4 text-slate-300 shrink-0" />
                      </button>
                    );
                  })}
                </div>
              )}
            </div>

            <div className="px-6 py-3 border-t border-slate-100 bg-slate-50">
              <button
                onClick={() => { setPropertyPickerService(null); setProperties([]); setCurrentPropertyId(null); }}
                className="text-sm text-slate-500 hover:text-slate-700 font-semibold"
              >
                Cancel
              </button>
            </div>
          </div>
        </>
      )}

      {/* ── Setup Progress ── */}
      <div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
        <div className="grid grid-cols-1 md:grid-cols-3 gap-px bg-slate-100">
          {/* Total connected */}
          <div className="bg-white px-6 py-5">
            <div className="flex items-center gap-3">
              <div className="relative w-14 h-14 shrink-0">
                <svg className="w-14 h-14 -rotate-90" viewBox="0 0 56 56">
                  <circle cx="28" cy="28" r="24" fill="none" stroke="#f1f5f9" strokeWidth="4" />
                  <circle cx="28" cy="28" r="24" fill="none" stroke="#DC2626" strokeWidth="4" strokeLinecap="round" strokeDasharray={`${(connectedCount / integrations.length) * 150.8} 150.8`} />
                </svg>
                <span className="absolute inset-0 flex items-center justify-center text-xs font-bold text-slate-900">{connectedCount}</span>
              </div>
              <div>
                <p className="text-sm font-bold text-slate-800">Total Connected</p>
                <p className="text-xs text-slate-500 mt-0.5">{connectedCount} of {integrations.length} integrations active</p>
              </div>
            </div>
          </div>

          {/* Recommended connected */}
          <div className="bg-white px-6 py-5">
            <div className="flex items-center gap-3">
              <div className="relative w-14 h-14 shrink-0">
                <svg className="w-14 h-14 -rotate-90" viewBox="0 0 56 56">
                  <circle cx="28" cy="28" r="24" fill="none" stroke="#f1f5f9" strokeWidth="4" />
                  <circle cx="28" cy="28" r="24" fill="none" stroke={recommendedConnected === recommendedItems.length ? "#16a34a" : "#d97706"} strokeWidth="4" strokeLinecap="round" strokeDasharray={`${(recommendedConnected / recommendedItems.length) * 150.8} 150.8`} />
                </svg>
                <span className="absolute inset-0 flex items-center justify-center text-xs font-bold text-slate-900">{recommendedConnected}</span>
              </div>
              <div>
                <p className="text-sm font-bold text-slate-800">Recommended</p>
                <p className="text-xs text-slate-500 mt-0.5">{recommendedConnected} of {recommendedItems.length} core integrations</p>
              </div>
            </div>
          </div>

          {/* Missing critical */}
          <div className="bg-white px-6 py-5">
            <div className="flex items-center gap-3">
              <div className={`w-14 h-14 rounded-full flex items-center justify-center shrink-0 ${missingCritical.length > 0 ? "bg-red-50" : "bg-green-50"}`}>
                {missingCritical.length > 0 ? (
                  <XCircle className="w-6 h-6 text-red-500" />
                ) : (
                  <CheckCircle2 className="w-6 h-6 text-green-500" />
                )}
              </div>
              <div>
                <p className="text-sm font-bold text-slate-800">
                  {missingCritical.length > 0 ? "Missing Critical" : "All Critical Connected"}
                </p>
                {missingCritical.length > 0 ? (
                  <div className="flex flex-wrap gap-1 mt-1">
                    {missingCritical.map((i) => (
                      <span key={i.id} className="text-[10px] font-semibold text-red-600 bg-red-50 border border-red-200 rounded-full px-2 py-0.5">{i.name}</span>
                    ))}
                  </div>
                ) : (
                  // Derive the green-box sub-text from CRITICAL_INTEGRATIONS
                  // dynamically. The previous hardcoded "GSC, GA4, and GBP
                  // are all active" string falsely claimed GBP was active
                  // because it didn't notice GBP isn't in the critical set
                  // (GBP is comingSoon: true). Building the list from the
                  // actual integrations whose ids are in CRITICAL_INTEGRATIONS
                  // keeps the text honest if the list ever changes.
                  (() => {
                    const criticalNames = integrations
                      .filter((i) => CRITICAL_INTEGRATIONS.includes(i.id))
                      .map((i) => i.name);
                    const total = criticalNames.length;
                    const list = total === 0
                      ? "all critical integrations"
                      : total === 1
                        ? criticalNames[0]
                        : total === 2
                          ? `${criticalNames[0]} and ${criticalNames[1]}`
                          : `${criticalNames.slice(0, -1).join(", ")}, and ${criticalNames[total - 1]}`;
                    const verb = total === 1 ? "is" : "are";
                    return (
                      <p className="text-xs text-green-600 mt-0.5">
                        {list} {verb} active ({total} of {total} critical connected)
                      </p>
                    );
                  })()
                )}
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* ── Recommended integrations (only show unconnected critical ones) ── */}
      {missingCriticalItems.length > 0 && (
      <div className="bg-gradient-to-r from-brand-50 to-violet-50 rounded-xl border border-brand-200 px-6 py-5">
        <div className="flex items-center gap-2 mb-1">
          <Sparkles className="w-4 h-4 text-brand-600" />
          <p className="text-sm font-bold text-slate-800">Recommended for {brand.name}</p>
        </div>
        <p className="text-xs text-slate-600 mb-4">Connect these core integrations to unlock full platform capabilities.</p>
        <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
          {missingCriticalItems.map((item) => {
            return (
              <div key={item.id} className="bg-white rounded-lg border border-slate-200 px-4 py-3 flex items-center gap-3">
                <div className="w-8 h-8 rounded-lg bg-brand-50 flex items-center justify-center shrink-0">
                  <Plug className="w-4 h-4 text-brand-600" />
                </div>
                <div className="flex-1 min-w-0">
                  <p className="text-xs font-semibold text-slate-800">{item.name}</p>
                  <p className="text-[10px] text-slate-400 truncate">{item.description}</p>
                </div>
                <button
                  onClick={() => isNangoManaged(item.id) ? connectNango(item.id) : item.comingSoon ? notifyMe(item.id) : toggleConnection(item.id)}
                  disabled={nangoConnecting === item.id}
                  className={`px-3 py-1.5 text-[10px] font-semibold rounded-lg border shrink-0 ${
                    isNangoManaged(item.id)
                      ? "bg-brand-600 text-white border-brand-600 hover:bg-brand-700"
                      : item.comingSoon
                        ? "bg-slate-100 text-slate-600 border-slate-200 hover:bg-slate-200"
                        : "bg-brand-600 text-white border-brand-600 hover:bg-brand-700"
                  } disabled:opacity-50`}
                >
                  {nangoConnecting === item.id ? "Connecting…" : isNangoManaged(item.id) ? "Connect" : item.comingSoon ? "Notify Me" : "Connect"}
                </button>
              </div>
            );
          })}
        </div>
      </div>
      )}

      {/* ── Filter buttons ── */}
      <div className="flex items-center gap-1.5 flex-wrap">
        {FILTERS.map((f) => (
          <button
            key={f.key}
            onClick={() => setFilter(f.key)}
            className={`px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors ${
              filter === f.key
                ? "bg-brand-600 text-white shadow-sm"
                : "bg-white border border-slate-200 text-slate-600 hover:bg-slate-50 hover:border-slate-300"
            }`}
          >
            {f.label}
          </button>
        ))}
      </div>

      {/* ── Sections ── */}
      {getSectionsToShow().map((sectionKey) => {
        const meta = SECTION_META[sectionKey];
        const CatIcon = meta.icon;
        const items = integrations.filter((i) => i.sections.includes(sectionKey) && matchesFilter(i));

        if (items.length === 0) return null;

        const sectionConnected = items.filter((i) => i.connected).length;
        const sortedItems = [...items].sort((a, b) => (isBroken(b) ? 1 : 0) - (isBroken(a) ? 1 : 0));

        return (
          <div key={sectionKey} className="bg-white rounded-xl border border-slate-200 overflow-hidden">
            <div className="px-6 py-4 border-b border-slate-100 flex items-center gap-2">
              <div className={`w-7 h-7 rounded-lg ${meta.bg} flex items-center justify-center shrink-0`}>
                <CatIcon className={`w-3.5 h-3.5 ${meta.accent}`} />
              </div>
              <h2 className="font-semibold text-slate-800 text-sm">{meta.label}</h2>
              <span className="ml-auto text-xs text-slate-400">
                {sectionConnected} of {items.length} connected
              </span>
            </div>

            <div className="divide-y divide-slate-100">
              {sortedItems.map((integration) => (
                <div key={integration.id} className={`px-6 py-4 flex items-center gap-4 transition-colors ${isBroken(integration) ? "bg-amber-50/30 border-l-2 border-amber-400 hover:bg-amber-50/50" : "hover:bg-slate-50/50"}`}>
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center gap-2 flex-wrap">
                      <span className="text-sm font-semibold text-slate-800">{integration.name}</span>
                      {/* Status badges */}
                      {integration.status === "connected" && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-green-700 bg-green-50 border border-green-200 rounded-full px-2 py-0.5">
                          <CheckCircle2 className="w-3 h-3" /> Connected
                        </span>
                      )}
                      {integration.status === "syncing" && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-blue-600 bg-blue-50 border border-blue-200 rounded-full px-2 py-0.5">
                          <RefreshCw className="w-3 h-3 animate-spin" /> Syncing
                        </span>
                      )}
                      {integration.status === "error" && !integration.tokenExpired && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-red-600 bg-red-50 border border-red-200 rounded-full px-2 py-0.5">
                          <AlertCircle className="w-3 h-3" /> Error
                        </span>
                      )}
                      {isBroken(integration) && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-amber-700 bg-amber-50 border border-amber-200 rounded-full px-2 py-0.5">
                          <Clock className="w-3 h-3" /> Token Expired
                        </span>
                      )}
                      {(!integration.connected && !integration.comingSoon && !isBroken(integration) && integration.status !== "error") && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-slate-400 bg-slate-100 rounded-full px-2 py-0.5">
                          <XCircle className="w-3 h-3" /> Not Connected
                        </span>
                      )}
                      {integration.comingSoon && !isNangoManaged(integration.id) && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-violet-600 bg-violet-50 border border-violet-200 rounded-full px-2 py-0.5">
                          <Clock className="w-3 h-3" /> Coming Soon
                        </span>
                      )}
                      {integration.comingSoon && isNangoManaged(integration.id) && !isNangoConnected(integration.id) && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-blue-600 bg-blue-50 border border-blue-200 rounded-full px-2 py-0.5">
                          <Plug className="w-3 h-3" /> Ready
                        </span>
                      )}
                      {isNangoConnected(integration.id) && (
                        <span className="inline-flex items-center gap-1 text-[10px] font-semibold text-emerald-600 bg-emerald-50 border border-emerald-200 rounded-full px-2 py-0.5">
                          <CheckCircle2 className="w-3 h-3" /> Connected via Nango
                        </span>
                      )}
                    </div>
                    <p className="text-xs text-slate-500 mt-0.5">{integration.description}</p>

                    {/* Metadata row: last synced + error details + retry info */}
                    {(integration.connected || isBroken(integration)) && (
                      <div className="flex items-center gap-3 mt-1.5 flex-wrap">
                        {integration.connected && integration.lastSynced && (
                          <span className="text-[10px] text-slate-400">Last synced: {integration.lastSynced}</span>
                        )}
                        {integration.status === "error" && integration.syncError && !integration.tokenExpired && (
                          <span className="text-[10px] font-semibold text-red-600 bg-red-50 px-2 py-0.5 rounded">
                            {integration.syncError}
                          </span>
                        )}
                        {isBroken(integration) && (
                          <span className="text-[10px] font-semibold text-amber-700 bg-amber-50 px-2 py-0.5 rounded">
                            Token expired — reconnect to resume data sync
                          </span>
                        )}
                        {integration.connected && (integration.syncRetryCount ?? 0) > 0 && integration.nextRetryAt && (
                          <span className="text-[10px] text-slate-400">
                            Retry {integration.syncRetryCount} · next at {new Date(integration.nextRetryAt).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit" })}
                          </span>
                        )}
                      </div>
                    )}
                  </div>
                  <div className="flex items-center gap-2 shrink-0">
                    {/* Manual Select Property trigger — fallback in case
                        the post-OAuth auto-open misses, and also the
                        way users change their GSC site / GA4 property
                        without disconnecting. Only renders for the two
                        Google services that have property selection. */}
                    {integration.connected && GOOGLE_OAUTH_IDS.includes(integration.id) && (
                      <button
                        onClick={() => openPropertyPicker(integration.id)}
                        className="flex items-center gap-1.5 px-3 py-2 text-xs font-semibold rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 transition-colors"
                        title="Change which property this integration syncs"
                      >
                        <BarChart3 className="w-3 h-3" />
                        Select Property
                      </button>
                    )}
                    {integration.connected && SYNCABLE_IDS.includes(integration.id) && (
                      <button
                        onClick={() => syncNow(integration.id)}
                        disabled={syncing === integration.id}
                        className="flex items-center gap-1.5 px-3 py-2 text-xs font-semibold rounded-lg border border-slate-200 text-slate-600 hover:bg-slate-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                      >
                        <RefreshCw className={`w-3 h-3 ${syncing === integration.id && !forceSyncing ? "animate-spin" : ""}`} />
                        {syncing === integration.id && !forceSyncing
                          ? "Syncing"
                          : syncing === integration.id && forceSyncing
                            ? "Re-syncing 90 days…"
                            : "Sync Now"}
                      </button>
                    )}
                    {integration.connected && integration.id === "gsc" && (
                      <button
                        onClick={() => confirmFullResync(integration.id)}
                        disabled={syncing === integration.id}
                        className="flex items-center gap-1.5 px-3 py-2 text-xs font-semibold rounded-lg border border-dashed border-slate-300 text-slate-500 hover:bg-slate-50 hover:border-slate-400 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
                        title="Delete cached data and pull 90 days from scratch"
                      >
                        <RotateCw className="w-3 h-3" />
                        Full Re-sync
                      </button>
                    )}
                    <button
                      onClick={() => toggleConnection(integration.id)}
                      disabled={syncing === integration.id}
                      className={`px-4 py-2 text-xs font-semibold rounded-lg border transition-colors disabled:opacity-50 ${
                        integration.comingSoon
                          ? "bg-slate-100 text-slate-600 border-slate-200 hover:bg-slate-200"
                          : isBroken(integration)
                            ? "bg-amber-600 text-white border-amber-600 hover:bg-amber-700"
                            : integration.connected
                              ? "bg-white text-slate-600 border-slate-200 hover:bg-slate-50"
                              : "bg-brand-600 text-white border-brand-600 hover:bg-brand-700"
                      }`}
                    >
                      {integration.comingSoon ? "Notify Me" : isBroken(integration) ? "Reconnect" : integration.connected ? "Disconnect" : "Connect"}
                    </button>
                  </div>
                </div>
              ))}
            </div>
          </div>
        );
      })}
      {/* GBP Location Picker Modal */}
      <GbpLocationPicker brandId={brand.id} open={gbpPickerOpen} onClose={() => setGbpPickerOpen(false)} onSaved={() => { loadIntegrationStatus(); setGbpLocationCount(null); }} />
    </div>
  );
}
