import { ArrowDownAZ, ArrowUpAZ, ChevronDown, Copy, Filter, Settings2, Sparkles, UserPlus, X } from 'lucide-react';

import { useEffect, useRef, useState } from 'react';

import { Head, Link, router, usePage } from '@inertiajs/react';

import AiJobProgress from '@/Components/Ai/AiJobProgress';
import BatchJobProgress from '@/Components/Batch/BatchJobProgress';
import { UpgradePrompt } from '@/Components/billing/UpgradePrompt';
import ActivationPromptCard from '@/Components/Connections/ActivationPromptCard';
import PageHeader from '@/Components/layout/PageHeader';
import SiteNav from '@/Components/Navigation/SiteNav';
import ActionTypeFilter from '@/Components/Recommendations/ActionTypeFilter';
import BatchGenerateModal from '@/Components/Recommendations/BatchGenerateModal';
import BulkActionsBar from '@/Components/Recommendations/BulkActionsBar';
import BulkPublishModal from '@/Components/Recommendations/BulkPublishModal';
import LifecycleStatusFilter from '@/Components/Recommendations/LifecycleStatusFilter';
import RecommendationCard from '@/Components/Recommendations/RecommendationCard';
import RecommendationCardSkeleton from '@/Components/Recommendations/RecommendationCardSkeleton';
import InertiaPagination from '@/Components/Shared/InertiaPagination';
import RecommendationsFirstVisitTour from '@/Components/Shared/RecommendationsFirstVisitTour';
import { Badge } from '@/Components/ui/badge';
import { Button } from '@/Components/ui/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/Components/ui/collapsible';
import { EmptyState } from '@/Components/ui/empty-state';
import { ExportButton } from '@/Components/ui/export-button';
import { InfoTooltip } from '@/Components/ui/info-tooltip';
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/Components/ui/sheet';
import { useMilestone } from '@/hooks/useMilestone';
import { usePolling } from '@/hooks/usePolling';
import { useSiteKeyboardShortcuts } from '@/hooks/useSiteKeyboardShortcuts';
import DashboardLayout from '@/Layouts/DashboardLayout';
import { trackFilterApplied, trackProductEvent } from '@/lib/analytics';
import { RECOMMENDATIONS_FIRST_VIEWED, REFERRAL_CTA_SHOWN, REFERRAL_LINK_COPIED } from '@/lib/event-catalog';
import { LIFECYCLE_STATUS_LABELS } from '@/lib/status';
import { cn } from '@/lib/utils';
import type { PageProps, SiteBasic } from '@/types';

interface Recommendation {
  id: number;
  page_url: string;
  action_type: string;
  impact_score: number;
  confidence_score: number;
  title: string;
  reasoning: string;
  evidence: {
    clicks_before: number;
    clicks_after: number;
    delta_percent: number;
    position_before?: number;
    position_after?: number;
  };
  has_draft: boolean;
  latest_draft_id: number | null;
  lifecycle_status: string;
}

interface PaginatedRecommendations {
  data: Recommendation[];
  links: Array<{ url: string | null; label: string; active: boolean }>;
  current_page: number;
  last_page: number;
}

interface AiJob {
  id: number;
  status: string;
  total_recommendations: number;
  completed_recommendations: number;
  progress_percent: number;
}

interface BatchJob {
  id: number;
  status: string;
  total_jobs: number;
  completed_jobs: number;
  failed_jobs: number;
  total_tokens: number;
  total_cost: number;
  progress_percent: number;
  estimated_seconds_remaining: number | null;
}

interface Props {
  site: SiteBasic;
  analysis_run: { id: number; status: string; completed_at: string | null } | null;
  recommendations: PaginatedRecommendations;
  filters: { action_type: string | null; lifecycle_status: string | null; sort: string | null };
  counts: Record<string, number>;
  ai_available: boolean;
  ai_job: AiJob | null;
  batch_job: BatchJob | null;
  ai_settings_summary: {
    model: string;
    temperature: number;
    top_p: number;
    max_output_tokens: number;
  };
  gsc_connected: boolean;
  gsc_data_coverage_days?: number | null;
  referral_url?: string | null;
}

const ACTION_TYPE_LABELS: Record<string, string> = {
  noindex: 'Noindex',
  content_rewrite: 'AI Draft',
  meta_tag_optimization: 'Meta Tags',
  internal_linking: 'Internal Linking',
  thin_content: 'Thin Content',
  cannibalization_consolidation: 'Consolidate',
  light_refresh: 'Light Refresh',
  section_refresh: 'Section Refresh',
  full_rewrite: 'Full Rewrite',
  reposition_intent: 'Reposition',
  eeat_improvement: 'E-E-A-T',
  geo_improvement: 'GEO',
};

function buildSortUrl(siteId: number, filters: Props['filters'], newSort: string | null): string {
  const baseUrl = route('recommendations.index', siteId);
  const params = new URLSearchParams();
  if (filters.action_type) params.set('action_type', filters.action_type);
  if (filters.lifecycle_status) params.set('lifecycle_status', filters.lifecycle_status);
  if (newSort) params.set('sort', newSort);
  const qs = params.toString();
  return qs ? `${baseUrl}?${qs}` : baseUrl;
}

function buildClearFilterUrl(
  siteId: number,
  filters: Props['filters'],
  keyToClear: string,
): string {
  const baseUrl = route('recommendations.index', siteId);
  const params = new URLSearchParams();
  if (filters.action_type && keyToClear !== 'action_type')
    params.set('action_type', filters.action_type);
  if (filters.lifecycle_status && keyToClear !== 'lifecycle_status')
    params.set('lifecycle_status', filters.lifecycle_status);
  if (filters.sort && keyToClear !== 'sort') params.set('sort', filters.sort);
  const qs = params.toString();
  return qs ? `${baseUrl}?${qs}` : baseUrl;
}

function buildQuickWinsUrl(siteId: number): string {
  const baseUrl = route('recommendations.index', siteId);
  return `${baseUrl}?lifecycle_status=pending&sort=impact_desc`;
}

export default function RecommendationsIndex({
  site,
  analysis_run: analysisRun,
  recommendations,
  filters,
  counts,
  ai_available: aiAvailable,
  ai_job: aiJob,
  batch_job: batchJob,
  ai_settings_summary: aiSettingsSummary,
  gsc_connected: gscConnected,
  gsc_data_coverage_days: gscDataCoverageDays,
  referral_url: referralUrl,
}: Props) {
  const { ai_max_batch_size, polling_interval_ms, ai_status, ai_consent, limits } =
    usePage<PageProps>().props;

  const aiUpsellMessage = !aiAvailable
    ? (() => {
        if (!ai_consent?.has_bundled_consent) {
          return 'Accept the AI processing agreement to start generating free drafts';
        }
        if (ai_status?.mode === 'bundled' && (ai_status?.bundled_remaining ?? 0) === 0) {
          return "You've used all your free AI drafts this month. Upgrade for more.";
        }
        return 'Set up AI to generate content drafts for these recommendations';
      })()
    : null;

  // ACT-003: First-time recommendations view milestone
  const { celebrate: celebrateFirstView, hasCelebrated: hasSeenRecommendations } = useMilestone(
    'first_recommendations_viewed',
  );
  const [firstViewBannerDismissed, setFirstViewBannerDismissed] = useState(false);
  const showFirstViewBanner =
    !hasSeenRecommendations && !firstViewBannerDismissed && recommendations.data.length > 0;

  // ACT-019-03: Referral nudge — localStorage-gated, shown once when user has ≥3 recommendations
  const REFERRAL_NUDGE_KEY = 'rec_referral_nudge_v1_dismissed';
  const [referralNudgeDismissed, setReferralNudgeDismissed] = useState(() => {
    if (typeof window === 'undefined') return true;
    return localStorage.getItem(REFERRAL_NUDGE_KEY) === '1';
  });
  const [referralCopied, setReferralCopied] = useState(false);
  const showReferralNudge =
    !!referralUrl && !referralNudgeDismissed && (counts.total ?? 0) >= 3;

  // Fire REFERRAL_CTA_SHOWN once when the nudge becomes visible
  const referralNudgeFiredRef = useRef(false);
  useEffect(() => {
    if (showReferralNudge && !referralNudgeFiredRef.current) {
      referralNudgeFiredRef.current = true;
      trackProductEvent(REFERRAL_CTA_SHOWN, { site_id: String(site.id), source: 'recommendations_page' });
    }
  }, [showReferralNudge, site.id]);

  // Track page view
  useEffect(() => {
    trackProductEvent('recommendations_viewed', {
      site_id: String(site.id),
      recommendation_count: recommendations.data.length,
      active_lifecycle_filter: filters.lifecycle_status ?? 'all',
      active_action_filter: filters.action_type ?? 'all',
    });
    if (!hasSeenRecommendations && recommendations.data.length > 0) {
      trackProductEvent(RECOMMENDATIONS_FIRST_VIEWED, { site_id: String(site.id) });
    }
    // intentional: fire-once page-view event on mount; site/filter props are stable
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // Track filter changes
  const prevFiltersRef = useRef(filters);
  useEffect(() => {
    const prev = prevFiltersRef.current;
    if (prev.lifecycle_status !== filters.lifecycle_status && filters.lifecycle_status) {
      trackFilterApplied('lifecycle_status', filters.lifecycle_status);
    }
    if (prev.action_type !== filters.action_type && filters.action_type) {
      trackFilterApplied('action_type', filters.action_type);
    }
    prevFiltersRef.current = filters;
  }, [filters]);

  const [batchModalOpen, setBatchModalOpen] = useState(false);
  const [publishModalOpen, setPublishModalOpen] = useState(false);
  const [selectedIds, setSelectedIds] = useState<number[]>([]);
  const [mobileFiltersOpen, setMobileFiltersOpen] = useState(false);
  const [aiSettingsOpen, setAiSettingsOpen] = useState(false);
  const filterButtonRef = useRef<HTMLButtonElement>(null);
  const quickWinsButtonRef = useRef<HTMLAnchorElement>(null);

  // Keyboard shortcuts
  const nextPageUrl = recommendations.links.find((link) => link.label === '&raquo;')?.url;
  const prevPageUrl = recommendations.links.find((link) => link.label === '&laquo;')?.url;

  useSiteKeyboardShortcuts({
    onSearch: () => {
      // Focus Quick Wins button on desktop, filter button on mobile
      if (quickWinsButtonRef.current) {
        quickWinsButtonRef.current.focus();
      } else {
        filterButtonRef.current?.focus();
      }
    },
    onNextPage: nextPageUrl ? () => router.visit(nextPageUrl) : undefined,
    onPrevPage: prevPageUrl ? () => router.visit(prevPageUrl) : undefined,
  });

  // Poll when AI job is active
  usePolling(!!aiJob && ['pending', 'processing'].includes(aiJob.status), polling_interval_ms, [
    'recommendations',
    'aiJob',
  ]);

  // Poll when batch job is active
  const batchStatus = batchJob?.status;
  useEffect(() => {
    if (batchStatus && ['pending', 'processing'].includes(batchStatus)) {
      const interval = setInterval(() => {
        router.reload({ only: ['recommendations', 'batchJob'] });
      }, polling_interval_ms);
      return () => clearInterval(interval);
    }
  }, [batchStatus, polling_interval_ms]);

  // Clear selection when recommendations change (e.g., after filtering or status update)
  useEffect(() => {
    setSelectedIds([]);
  }, [recommendations.data]);

  const handleToggleSelection = (id: number) => {
    setSelectedIds((prev) =>
      prev.includes(id) ? prev.filter((selectedId) => selectedId !== id) : [...prev, id],
    );
  };

  const handleClearSelection = () => {
    setSelectedIds([]);
  };

  const handlePublishClick = () => {
    setPublishModalOpen(true);
  };

  // Get draft IDs for selected recommendations that have drafts
  const selectedDraftIds = recommendations.data
    .filter((r) => selectedIds.includes(r.id) && r.has_draft && r.latest_draft_id !== null)
    .map((r) => r.latest_draft_id as number);

  const hasAnyDrafts = selectedDraftIds.length > 0;

  const isAscSort = filters.sort === 'impact_asc';
  const toggleSortUrl = buildSortUrl(site.id, filters, isAscSort ? null : 'impact_asc');
  const clearAllFiltersUrl = route('recommendations.index', site.id);

  const isQuickWinsActive =
    filters.lifecycle_status === 'pending' && !filters.action_type && filters.sort !== 'impact_asc';

  // Active filter chips
  const activeFilterChips: Array<{ key: string; label: string; clearUrl: string }> = [];
  if (filters.action_type) {
    activeFilterChips.push({
      key: 'action_type',
      label: `Type: ${ACTION_TYPE_LABELS[filters.action_type] ?? filters.action_type}`,
      clearUrl: buildClearFilterUrl(site.id, filters, 'action_type'),
    });
  }
  if (filters.lifecycle_status) {
    activeFilterChips.push({
      key: 'lifecycle_status',
      label: `Status: ${LIFECYCLE_STATUS_LABELS[filters.lifecycle_status] ?? filters.lifecycle_status}`,
      clearUrl: buildClearFilterUrl(site.id, filters, 'lifecycle_status'),
    });
  }
  if (filters.sort) {
    activeFilterChips.push({
      key: 'sort',
      label: `Sort: ${filters.sort === 'impact_asc' ? 'Low to High' : 'High to Low'}`,
      clearUrl: buildClearFilterUrl(site.id, filters, 'sort'),
    });
  }

  const hasActiveFilters = activeFilterChips.length > 0;

  // Export params from current filters
  const exportParams: Record<string, string> = {};
  if (filters.action_type) exportParams.action_type = filters.action_type;
  if (filters.lifecycle_status) exportParams.lifecycle_status = filters.lifecycle_status;

  return (
    <>
      <Head title={`${site.name} - Recommendations`} />

      <PageHeader
        title="Recommendations"
        subtitle={`${site.name} · ${site.domain}`}
        actions={
          <>
            {analysisRun && counts.total > 0 && (
              <Button variant={isQuickWinsActive ? 'default' : 'outline'} size="sm" asChild>
                <Link ref={quickWinsButtonRef} href={buildQuickWinsUrl(site.id)}>
                  <Sparkles className="h-4 w-4 mr-1" />
                  Quick Wins
                </Link>
              </Button>
            )}
            {analysisRun && counts.total > 0 && (
              <ExportButton
                href={route('export.recommendations', site.id)}
                params={Object.keys(exportParams).length > 0 ? exportParams : undefined}
                label="Export CSV"
                variant="outline"
                size="sm"
              />
            )}
            {analysisRun &&
              counts.total > 0 &&
              aiAvailable &&
              (counts.content_rewrite ?? 0) > 0 && (
                <Button onClick={() => setBatchModalOpen(true)}>
                  Generate All Drafts ({counts.content_rewrite ?? 0})
                </Button>
              )}
          </>
        }
      />

      <div className="container py-6">
        <SiteNav
          siteId={site.id}
          canAnalyze
          canRecommend
          canCannibalization
          canOpportunityMap
          canGeographic
          canDevice
        />

        {aiUpsellMessage && (
          <div className="flex items-start gap-3 rounded-lg border border-warning/30 bg-warning/10 px-4 py-3 mb-4">
            <Sparkles className="h-4 w-4 text-warning mt-0.5 shrink-0" />
            <div className="flex-1 min-w-0">
              <p className="text-sm font-medium text-warning">{aiUpsellMessage}</p>
            </div>
            <Button
              asChild
              size="sm"
              variant="outline"
              className="shrink-0 border-warning/30 text-warning hover:bg-warning/10"
            >
              <Link href={route('settings.ai')}>Set Up Now</Link>
            </Button>
          </div>
        )}

        {/* BILL-005: Drafts upgrade prompt at 80% usage — highest-intent surface */}
        {limits && limits.max_drafts_per_month !== null && limits.max_drafts_per_month > 0 && (
          <div className="mb-4">
            <UpgradePrompt
              limitType="drafts"
              currentUsage={limits.drafts_used_this_month}
              maxUsage={limits.max_drafts_per_month}
              proLimit={limits.pro_drafts_per_month}
            />
          </div>
        )}

        {!analysisRun && !gscConnected && (
          <ActivationPromptCard variant="connect_gsc" siteId={site.id} siteName={site.name} />
        )}

        {!analysisRun && gscConnected && (
          <ActivationPromptCard variant="run_analysis" siteId={site.id} siteName={site.name} />
        )}

        {analysisRun && counts.total === 0 && (
          <EmptyState
            icon={Sparkles}
            title={
              gscDataCoverageDays !== null && gscDataCoverageDays !== undefined && gscDataCoverageDays < 56
                ? 'Limited GSC history'
                : 'No recommendations yet'
            }
            description={
              gscDataCoverageDays !== null && gscDataCoverageDays !== undefined && gscDataCoverageDays < 56
                ? `Your site only has ${gscDataCoverageDays} day${gscDataCoverageDays !== 1 ? 's' : ''} of GSC history — analysis works best with 60+ days. Results improve as your data accumulates. Check back in ${Math.max(1, 56 - gscDataCoverageDays)} more day${56 - gscDataCoverageDays !== 1 ? 's' : ''}.`
                : 'Your site looks healthy for this time window. RankWiz surfaces recommendations when it spots traffic changes, thin content, or optimization opportunities.'
            }
            action={
              <>
                <p className="text-sm text-muted-foreground mt-2">
                  {gscDataCoverageDays !== null && gscDataCoverageDays !== undefined && gscDataCoverageDays < 56
                    ? 'In the meantime, explore other growth paths:'
                    : 'Try a wider time window, or explore other growth paths:'}
                </p>
                {/* ACT-009: Alternative value paths */}
                <div className="mt-4 grid grid-cols-1 gap-2 sm:grid-cols-3">
                  <Button asChild variant="outline" size="sm">
                    <Link href={route('opportunity-map.index', site.id)}>
                      Keyword Opportunities
                    </Link>
                  </Button>
                  <Button asChild variant="outline" size="sm">
                    <Link href={route('topic-clusters.index', site.id)}>Content Intelligence</Link>
                  </Button>
                  <Button asChild variant="outline" size="sm">
                    <Link href={route('freshness.index', site.id)}>Content Freshness</Link>
                  </Button>
                </div>
                <Button asChild className="mt-3">
                  <Link href={route('analyze.index', site.id)}>Try a Different Time Window</Link>
                </Button>
              </>
            }
          />
        )}

        {analysisRun && counts.total > 0 && (
          <>
            {/* ACT-003: First-view banner — shows once until dismissed */}
            {showFirstViewBanner && (
              <div className="flex items-start gap-3 rounded-lg border border-primary/30 bg-primary/5 px-4 py-3 mb-4">
                <Sparkles className="h-4 w-4 text-primary mt-0.5 shrink-0" />
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium text-primary">
                    You have {counts.total} recommendation{counts.total !== 1 ? 's' : ''} ready to
                    review!
                  </p>
                  {recommendations.data[0] && (
                    <p className="text-xs text-muted-foreground mt-0.5">
                      Top opportunity: impact score{' '}
                      {Math.round(recommendations.data[0].impact_score * 100)}
                    </p>
                  )}
                </div>
                {recommendations.data[0] && (
                  <div className="flex items-center gap-2 shrink-0">
                    <Button asChild size="sm" variant="outline" className="shrink-0">
                      <Link
                        href={route('recommendations.show', [site.id, recommendations.data[0].id])}
                      >
                        Approve &amp; Track
                      </Link>
                    </Button>
                    <Button asChild size="sm" className="shrink-0">
                      <Link
                        href={route('recommendations.show', [site.id, recommendations.data[0].id])}
                      >
                        Generate AI Draft
                      </Link>
                    </Button>
                  </div>
                )}
                <button
                  type="button"
                  aria-label="Dismiss"
                  className="shrink-0 text-muted-foreground hover:text-foreground"
                  onClick={() => {
                    setFirstViewBannerDismissed(true);
                    celebrateFirstView('First recommendations milestone reached!');
                  }}
                >
                  <X className="h-4 w-4" />
                </button>
              </div>
            )}

            {batchJob && ['pending', 'processing', 'completed'].includes(batchJob.status) && (
              <BatchJobProgress batchJob={batchJob} />
            )}

            {aiJob && ['pending', 'processing'].includes(aiJob.status) && (
              <AiJobProgress aiJob={aiJob} />
            )}

            {/* AI Settings — collapsible */}
            {aiSettingsSummary && (
              <Collapsible open={aiSettingsOpen} onOpenChange={setAiSettingsOpen} className="mb-4">
                <CollapsibleTrigger asChild>
                  <button
                    type="button"
                    className="flex w-full items-center justify-between rounded-lg border bg-muted/50 px-3 py-2 text-sm text-muted-foreground hover:bg-muted transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
                  >
                    <span className="flex items-center gap-1.5">
                      <Settings2 className="h-3.5 w-3.5" />
                      AI Settings:{' '}
                      <strong className="text-foreground">{aiSettingsSummary.model}</strong>
                    </span>
                    <ChevronDown
                      className={cn(
                        'h-4 w-4 transition-transform duration-200',
                        aiSettingsOpen && 'rotate-180',
                      )}
                    />
                  </button>
                </CollapsibleTrigger>
                <CollapsibleContent>
                  <div className="rounded-b-lg border border-t-0 bg-muted/50 px-3 py-3 text-sm text-muted-foreground flex flex-wrap items-center gap-x-3 gap-y-1">
                    <span className="inline-flex items-center gap-1">
                      Model: <strong>{aiSettingsSummary.model}</strong>
                      <InfoTooltip
                        content="The AI model used for content generation. More capable models may produce better results but cost more per token."
                        side="top"
                      />
                    </span>
                    <span>&bull;</span>
                    <span className="inline-flex items-center gap-1">
                      Temp: {aiSettingsSummary.temperature}
                      <InfoTooltip
                        content="Controls randomness. Lower values (0.0-0.3) produce focused, deterministic output. Higher values (0.7-2.0) produce more creative, varied output."
                        side="top"
                      />
                    </span>
                    <span>&bull;</span>
                    <span className="inline-flex items-center gap-1">
                      Top P: {aiSettingsSummary.top_p}
                      <InfoTooltip
                        content="Controls diversity via nucleus sampling. 1.0 considers all tokens. Lower values (e.g. 0.1) only consider the most likely tokens."
                        side="top"
                      />
                    </span>
                    <span>&bull;</span>
                    <span className="inline-flex items-center gap-1">
                      Max tokens: {aiSettingsSummary.max_output_tokens}
                      <InfoTooltip
                        content="Maximum length of the generated content in tokens. One token is roughly 4 characters of English text."
                        side="top"
                      />
                    </span>
                  </div>
                </CollapsibleContent>
              </Collapsible>
            )}

            {/* Desktop filters (hidden on mobile) */}
            <div className="hidden sm:block">
              <ActionTypeFilter
                counts={counts}
                currentFilter={filters.action_type}
                siteId={site.id}
                currentFilters={{ lifecycle_status: filters.lifecycle_status, sort: filters.sort }}
              />

              <div className="mt-4 flex flex-wrap items-center gap-4">
                <div className="flex items-center gap-2">
                  <LifecycleStatusFilter
                    counts={counts}
                    currentFilter={filters.lifecycle_status}
                    siteId={site.id}
                    currentFilters={{ action_type: filters.action_type, sort: filters.sort }}
                  />
                  <InfoTooltip
                    content="Status flow: Pending → Reviewed → Approved → Applied → Tracking. Rejected and Deferred are terminal states."
                    side="right"
                  />
                </div>
              </div>
            </div>

            {/* Mobile filter button + Sheet */}
            <div className="sm:hidden mb-4">
              <Sheet open={mobileFiltersOpen} onOpenChange={setMobileFiltersOpen}>
                <SheetTrigger asChild>
                  <Button ref={filterButtonRef} variant="outline" size="sm">
                    <Filter className="h-4 w-4 mr-2" />
                    Filters
                    {hasActiveFilters && (
                      <Badge
                        variant="secondary"
                        className="ml-2 h-5 w-5 rounded-full p-0 flex items-center justify-center text-xs"
                      >
                        {activeFilterChips.length}
                      </Badge>
                    )}
                  </Button>
                </SheetTrigger>
                <SheetContent side="bottom" className="max-h-[80vh] overflow-y-auto">
                  <SheetHeader>
                    <SheetTitle>Filters</SheetTitle>
                  </SheetHeader>
                  <div className="mt-6 space-y-6">
                    <div>
                      <h3 className="text-sm font-medium mb-3">Action Type</h3>
                      <ActionTypeFilter
                        counts={counts}
                        currentFilter={filters.action_type}
                        siteId={site.id}
                        currentFilters={{
                          lifecycle_status: filters.lifecycle_status,
                          sort: filters.sort,
                        }}
                      />
                    </div>
                    <div>
                      <h3 className="text-sm font-medium mb-3">Status</h3>
                      <LifecycleStatusFilter
                        counts={counts}
                        currentFilter={filters.lifecycle_status}
                        siteId={site.id}
                        currentFilters={{ action_type: filters.action_type, sort: filters.sort }}
                      />
                    </div>
                  </div>
                </SheetContent>
              </Sheet>
            </div>

            {/* Active filter chips */}
            {hasActiveFilters && (
              <div className="mt-3 flex flex-wrap items-center gap-2">
                {activeFilterChips.map((chip) => (
                  <Badge
                    key={chip.key}
                    variant="secondary"
                    className="flex items-center gap-1 pr-1"
                  >
                    {chip.label}
                    <Link
                      href={chip.clearUrl}
                      className="ml-1 rounded-sm hover:bg-muted-foreground/20 p-0.5"
                      aria-label={`Remove ${chip.label} filter`}
                    >
                      <X className="h-3 w-3" />
                    </Link>
                  </Badge>
                ))}
                <Link
                  href={clearAllFiltersUrl}
                  className="text-xs text-muted-foreground hover:text-foreground transition-colors"
                >
                  Clear all
                </Link>
              </div>
            )}

            <div className="mt-4 flex items-center justify-between">
              <p className="text-sm text-muted-foreground">
                {recommendations.data.length > 0 && (
                  <>
                    Showing {recommendations.data.length} of {counts.total}
                  </>
                )}
              </p>
              <Button variant="ghost" size="sm" asChild>
                <Link href={toggleSortUrl}>
                  {isAscSort ? (
                    <ArrowUpAZ className="h-4 w-4 mr-1" />
                  ) : (
                    <ArrowDownAZ className="h-4 w-4 mr-1" />
                  )}
                  Impact {isAscSort ? 'Low to High' : 'High to Low'}
                </Link>
              </Button>
            </div>

            <div className="space-y-4 mt-2">
              {(batchJob && ['pending', 'processing'].includes(batchJob.status)) ||
              (aiJob && ['pending', 'processing'].includes(aiJob.status)) ? (
                <>
                  {Array.from({
                    length: Math.min(batchJob?.total_jobs || aiJob?.total_recommendations || 5, 10),
                  }).map((_, i) => (
                    <RecommendationCardSkeleton key={i} />
                  ))}
                </>
              ) : recommendations.data.length === 0 ? (
                /* UX-016: Filter adjustment guidance for empty filtered state */
                <EmptyState
                  icon={Filter}
                  title="No recommendations match your filters"
                  description="Your active filters are hiding all results. Try removing a filter to see more — for example, clear the action type or status filter."
                  action={
                    <>
                      <p className="text-sm text-muted-foreground mt-1">
                        Try broadening your filters to see more results.
                      </p>
                      <Link
                        href={clearAllFiltersUrl}
                        className="text-sm text-primary hover:underline mt-2 inline-block"
                      >
                        Clear all filters
                      </Link>
                    </>
                  }
                  size="sm"
                />
              ) : (
                recommendations.data.map((rec) => (
                  <RecommendationCard
                    key={rec.id}
                    recommendation={rec}
                    siteId={site.id}
                    aiAvailable={aiAvailable}
                    selected={selectedIds.includes(rec.id)}
                    onToggleSelection={() => handleToggleSelection(rec.id)}
                  />
                ))
              )}
            </div>

            {/* ACT-019-03: Referral nudge — shown once after user has ≥3 recommendations */}
            {showReferralNudge && (
              <div className="flex items-start gap-3 rounded-lg border border-border bg-muted/30 px-4 py-3 mt-4">
                <UserPlus className="h-4 w-4 text-muted-foreground mt-0.5 shrink-0" />
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium">Getting value from RankWiz?</p>
                  <p className="text-xs text-muted-foreground mt-0.5">
                    Share with a colleague — you both get 5 extra AI drafts.
                  </p>
                  <button
                    type="button"
                    className="mt-2 inline-flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-xs font-medium hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                    onClick={async () => {
                      await navigator.clipboard.writeText(referralUrl!);
                      setReferralCopied(true);
                      trackProductEvent(REFERRAL_LINK_COPIED, { site_id: String(site.id), source: 'recommendations_page' });
                      setTimeout(() => setReferralCopied(false), 2000);
                    }}
                  >
                    <Copy className="h-3 w-3" />
                    {referralCopied ? 'Copied!' : 'Copy referral link'}
                  </button>
                </div>
                <button
                  type="button"
                  aria-label="Dismiss referral nudge"
                  className="shrink-0 text-muted-foreground hover:text-foreground"
                  onClick={() => {
                    localStorage.setItem(REFERRAL_NUDGE_KEY, '1');
                    setReferralNudgeDismissed(true);
                  }}
                >
                  <X className="h-4 w-4" />
                </button>
              </div>
            )}

            <InertiaPagination
              links={recommendations.links}
              currentPage={recommendations.current_page}
              lastPage={recommendations.last_page}
            />
          </>
        )}

        {analysisRun && (
          <BatchGenerateModal
            open={batchModalOpen}
            onOpenChange={setBatchModalOpen}
            siteId={site.id}
            recommendationIds={
              selectedIds.length > 0
                ? selectedIds
                : recommendations.data
                    .filter(
                      (r) =>
                        r.action_type === 'content_rewrite' &&
                        ['pending', 'approved'].includes(r.lifecycle_status),
                    )
                    .map((r) => r.id)
            }
            maxBatchSize={ai_max_batch_size}
            aiSettingsSummary={aiSettingsSummary}
          />
        )}
      </div>

      <BulkActionsBar
        selectedIds={selectedIds}
        siteId={site.id}
        onClearSelection={handleClearSelection}
        onPublishClick={handlePublishClick}
        hasAnyDrafts={hasAnyDrafts}
      />

      {analysisRun && (
        <BulkPublishModal
          open={publishModalOpen}
          onOpenChange={setPublishModalOpen}
          siteId={site.id}
          draftIds={selectedDraftIds}
        />
      )}

      {/* ACT-005: First-visit spotlight tour — fixed bottom card, shows once */}
      <RecommendationsFirstVisitTour
        recommendationsCount={recommendations.data.length}
        showFirstViewBanner={showFirstViewBanner}
        opportunityMapUrl={route('opportunity-map.index', site.id)}
      />
    </>
  );
}

RecommendationsIndex.layout = (page: React.ReactNode) => <DashboardLayout>{page}</DashboardLayout>;
