import { useState, useEffect, useRef, useMemo } from 'react'
import { Database, Search, Play, Square, Trash2, RefreshCw, FileText, ShoppingBag, File, CreditCard, Loader2, ChevronDown, ChevronUp, Info, Zap, HelpCircle, X, Plus, Save, Clock, ToggleLeft, ToggleRight } from 'lucide-react'
import { PageHeader } from '@/components/layout/PageHeader'
import { Card, CardContent } from '@/components/common/Card'
import { Button } from '@/components/common/Button'
import { Badge } from '@/components/common/Badge'
import { PageLoading } from '@/components/common/Spinner'
import {
  useIndexingStatus,
  useStopIndexing,
  usePauseIndexing,
  useResumeIndexing,
  useKickIndexing,
  useRetryAllFailed,
  useIndexQueue,
  useRetryQueueItem,
  useClearIndex,
  useIndexingCreditsStatus,
} from '@/hooks/useKnowledgeBase'
import {
  useIndexRules,
  useScopeOptions,
  useSaveAndIndex,
  useTaxonomyTerms,
  useScopePosts,
  useReindexStale,
} from '@/hooks/useIndexRules'
import type { IndexRule, ScopeOption, TaxonomyTerm, ScopePostItem } from '@/hooks/useIndexRules'
import { useSettings, useUpdateSettings } from '@/hooks/useSettings'
import { useCreditsStatus } from '@/hooks/useUsage'
import { formatCredits } from '@/utils/formatters'
import { useLanguage } from '@/contexts/LanguageContext'
import { t, sprintf, isRtl } from '@/lib/i18n'

function siteStatusLabel(status?: 'active' | 'blocked' | 'disabled') {
  if (!status) return ''
  if (status === 'active') return t('Active')
  if (status === 'blocked') return t('Blocked')
  if (status === 'disabled') return t('Disabled')
  return status
}



// ── Accordion ────────────────────────────────────────────────
function Accordion({ title, icon, children, defaultOpen = false }: { title: string; icon?: React.ReactNode; children: React.ReactNode; defaultOpen?: boolean }) {
  const [open, setOpen] = useState(defaultOpen)
  return (
    <div className="border border-gray-200 rounded-lg overflow-hidden">
      <button type="button" onClick={() => setOpen(!open)} className="w-full flex items-center justify-between px-4 py-3 bg-gray-50 hover:bg-gray-100 transition-colors text-right">
        <span className="flex items-center gap-2 text-sm font-medium text-gray-800">{icon}{title}</span>
        {open ? <ChevronUp className="w-4 h-4 text-gray-500" /> : <ChevronDown className="w-4 h-4 text-gray-500" />}
      </button>
      {open && <div className="px-4 py-3 text-sm text-gray-700 bg-white">{children}</div>}
    </div>
  )
}

// ════════════════════════════════════════════════════════════════
// Main Page
// ════════════════════════════════════════════════════════════════

export function KnowledgeBase() {
  const { data: statusData, isLoading: statusLoading } = useIndexingStatus()
  const stopIndexing = useStopIndexing()
  const pauseIndexing = usePauseIndexing()
  const resumeIndexing = useResumeIndexing()
  const kickIndexing = useKickIndexing()
  const retryAllFailed = useRetryAllFailed()
  const retryQueueItem = useRetryQueueItem()
  const clearIndex = useClearIndex()

  const indexingCredits = useIndexingCreditsStatus()
  const convoCredits = useCreditsStatus()

  const isBaseLoading = statusLoading
  const failedQueue = useIndexQueue(1, 25, 'failed', !isBaseLoading)
  const skippedQueue = useIndexQueue(1, 25, 'skipped', !isBaseLoading)

  const lastKickRef = useRef<number>(0)
  const statusRef = statusData?.data
  const isIndexingForWatchdog = statusRef?.is_indexing || false
  const pausedForWatchdog = !!statusRef?.paused
  const queueStatsForWatchdog = statusRef?.queue_stats

  useEffect(() => {
    if (!isIndexingForWatchdog || pausedForWatchdog) return
    if ((queueStatsForWatchdog?.pending ?? 0) <= 0 || (queueStatsForWatchdog?.processing ?? 0) > 0) return
    const now = Date.now()
    if (now - lastKickRef.current < 12_000) return
    lastKickRef.current = now
    kickIndexing.mutate()
  }, [isIndexingForWatchdog, pausedForWatchdog, queueStatsForWatchdog?.pending, queueStatsForWatchdog?.processing, kickIndexing])

  if (isBaseLoading) return <PageLoading />

  const status = statusData?.data
  const isIndexing = status?.is_indexing || false
  const job = status?.job
  const queueStats = status?.queue_stats
  const paused = !!status?.paused
  const credits = indexingCredits.data?.data
  const remainingBoostCredits = (credits as any)?.boost_credits?.remaining ?? credits?.indexing_credits?.remaining ?? null
  const convo = convoCredits.data
  const convoBlocked = convo ? !convo.allowed : false
  const convoSiteStatus = convo?.site_status
  const totalConversationRemaining = (convo as any)?.boost_credits?.remaining ?? (convo?.credits?.free?.remaining ?? 0) + (convo?.credits?.subscription?.remaining ?? 0) + (convo?.credits?.purchased?.remaining ?? 0)
  const indexingBlocked = credits ? !credits.allowed : false

  const handleStopIndexing = async () => {
    if (!window.confirm(t('Stop indexing?'))) return
    try { await stopIndexing.mutateAsync() } catch (error: any) { alert(error.message || t('Error')) }
  }
  const handleClearIndex = async () => {
    if (!window.confirm(t('Delete the entire index? This action is irreversible.'))) return
    try { await clearIndex.mutateAsync(); alert(t('Index deleted')) } catch (error: any) { alert(error.message || t('Error')) }
  }

  return (
    <div className="space-y-5 animate-fade-in">
      <PageHeader title={t('Knowledge Base')} subtitle={t('Manage content for bot semantic search')} />

      {/* ─── Credits + Stats bar ─── */}
      <div className="flex flex-wrap gap-3 items-stretch">
        <div className={`flex-1 min-w-[180px] rounded-lg border p-3 ${convoBlocked || indexingBlocked ? 'border-amber-200 bg-amber-50' : 'border-gray-200 bg-gray-50'}`}>
          <div className="flex items-center gap-2 mb-1">
            <CreditCard className="w-4 h-4 text-gray-600" />
            <span className="text-xs font-medium text-gray-600">{t('BoostCoin')}</span>
            {(convoSiteStatus || credits?.site_status) && <Badge variant={(convoSiteStatus || credits?.site_status) === 'active' ? 'success' : 'warning'} className="text-[10px]">{siteStatusLabel(convoSiteStatus || credits?.site_status)}</Badge>}
          </div>
          <div className="text-xl font-bold text-gray-900">{formatCredits(remainingBoostCredits ?? totalConversationRemaining ?? 0)}</div>
          <div className="text-[10px] text-gray-500">{t('Unified balance for conversations and indexing')}</div>
        </div>
        {[
          { label: t('Pages'), icon: <File className="w-4 h-4 text-green-500" />, val: status?.index_stats?.by_type?.page, color: 'text-green-600' },
          { label: t('Posts'), icon: <FileText className="w-4 h-4 text-blue-500" />, val: status?.index_stats?.by_type?.post, color: 'text-blue-600' },
          { label: t('Products'), icon: <ShoppingBag className="w-4 h-4 text-purple-500" />, val: status?.index_stats?.by_type?.product, color: 'text-purple-600' },
        ].map(s => (
          <div key={s.label} className="bg-gray-50 rounded-lg p-3 border border-gray-200 text-center min-w-[80px]">
            <div className="flex justify-center mb-1">{s.icon}</div>
            <div className={`text-lg font-bold ${s.color}`}>{s.val || 0}</div>
            <div className="text-[10px] text-gray-500">{t('Indexed')}</div>
          </div>
        ))}
        {status?.last_full_index && (
          <div className="bg-gray-50 rounded-lg p-3 border border-gray-200 flex flex-col justify-center min-w-[100px]">
            <div className="text-[10px] text-gray-500">{t('Last Index')}</div>
            <div className="text-xs font-medium text-gray-700">{new Date(status.last_full_index).toLocaleDateString(isRtl() ? 'he-IL' : 'en-US')}</div>
            <div className="text-[10px] text-gray-400">{new Date(status.last_full_index).toLocaleTimeString(isRtl() ? 'he-IL' : 'en-US', { hour: '2-digit', minute: '2-digit' })}</div>
          </div>
        )}
      </div>

      {/* ═══════════════════════════════════════════════════════════
           SECTION 1: Indexing Rules (Elementor-style rule builder)
         ═══════════════════════════════════════════════════════════ */}
      <IndexRulesSection
        isIndexing={isIndexing}
        paused={paused}
        job={job}
        queueStats={queueStats}
        handleStopIndexing={handleStopIndexing}
        handleClearIndex={handleClearIndex}
        stopPending={stopIndexing.isPending}
        clearPending={clearIndex.isPending}
        pauseIndexing={() => pauseIndexing.mutate()}
        resumeIndexing={() => resumeIndexing.mutate()}
        pausePending={pauseIndexing.isPending || resumeIndexing.isPending}
      />

      {/* ═══════════════════════════════════════════════════════════
           SECTION 2: Auto Re-Index
         ═══════════════════════════════════════════════════════════ */}
      <AutoReIndexSection isIndexing={isIndexing} />

      {/* ═══════════════════════════════════════════════════════════
           SECTION 3: Info & Help
         ═══════════════════════════════════════════════════════════ */}
      <div className="space-y-2">
        <Accordion title={t('What is indexing and semantic search?')} icon={<Info className="w-4 h-4 text-blue-500" />}>
          <div className="space-y-2">
            <p>{t('Indexing is the process where BoostBot scans your site content and saves it to the knowledge base so the bot can answer more accurately.')}</p>
            <p>{t('With deep indexing, we vectorize the text to enable semantic search — finding answers even with different phrasing.')}</p>
          </div>
        </Accordion>
        <Accordion title={t('How does auto indexing work?')} icon={<Zap className="w-4 h-4 text-purple-500" />}>
          <div className="space-y-2">
            <p>{t('When you click "Save & Index", the system saves the filter rules and starts an incremental deep index.')}</p>
            <ol className="list-decimal list-inside space-y-1 text-gray-600">
              <li>{t('Content already indexed and unchanged — skipped (zero BoostCoin)')}</li>
              <li>{t('New or changed content — re-indexed')}</li>
              <li>{t('Content excluded by rules — not indexed')}</li>
            </ol>
            <div className="p-3 bg-purple-50 rounded-lg border border-purple-200 mt-2">
              <p className="text-purple-800"><strong>{t('Tip:')}</strong> {t('Change rules at any time — the button always runs an incremental index that saves BoostCoin.')}</p>
            </div>
          </div>
        </Accordion>
        <Accordion title={t('How are BoostCoin consumed?')} icon={<CreditCard className="w-4 h-4 text-amber-500" />}>
          <div className="space-y-2">
            <p>{t('Model: text-embedding-3-small ($0.02 / 1M tokens). 1 BoostCoin covers ~2 million tokens.')}</p>
            <div className="p-3 bg-amber-50 rounded-lg border border-amber-200">
              <p className="font-medium text-amber-900">{t('Example:')}</p>
              <p className="text-amber-800">{t('100 pages × 5,000 characters = ~166K tokens = ~0.083 BoostCoin')}</p>
            </div>
          </div>
        </Accordion>
        {(queueStats?.failed || queueStats?.skipped) ? (
          <Accordion title={sprintf(t('Failed / Skipped items (%s)'), String((queueStats?.failed ?? 0) + (queueStats?.skipped ?? 0)))} icon={<HelpCircle className="w-4 h-4 text-amber-500" />}>
            <div className="space-y-3">
              {(queueStats?.failed ?? 0) > 0 && <Button variant="outline" onClick={() => retryAllFailed.mutate()} loading={retryAllFailed.isPending}><RefreshCw className="w-4 h-4 me-1" />{t('Retry All')}</Button>}
              {failedQueue.data?.data?.items?.map((it: any) => (
                <div key={it.id} className="flex items-start justify-between gap-3 p-2 bg-amber-50 rounded-lg text-sm">
                  <div><div className="text-gray-900">#{it.id} • {it.content_type} {it.content_id}</div>{it.last_error && <div className="text-xs text-gray-600 mt-1">{it.last_error}</div>}</div>
                  <Button variant="outline" onClick={() => retryQueueItem.mutate(Number(it.id))} loading={retryQueueItem.isPending} className="text-xs">{t('Retry')}</Button>
                </div>
              ))}
              {skippedQueue.data?.data?.items?.map((it: any) => (
                <div key={it.id} className="p-2 bg-gray-50 rounded-lg text-sm"><div className="text-gray-900">#{it.id} • {it.content_type} {it.content_id} ({t('skipped')})</div></div>
              ))}
            </div>
          </Accordion>
        ) : null}
      </div>
    </div>
  )
}

// ════════════════════════════════════════════════════════════════
// Section 1: Indexing Rules (Elementor-style condition builder)
// ════════════════════════════════════════════════════════════════

function IndexRulesSection({ isIndexing, paused, job, queueStats, handleStopIndexing, handleClearIndex, stopPending, clearPending, pauseIndexing, resumeIndexing, pausePending }: any) {
  const { currentLanguage } = useLanguage()
  // Load saved rules + scope options
  const rulesQuery = useIndexRules(currentLanguage)
  const scopeQuery = useScopeOptions()
  const saveAndIndex = useSaveAndIndex(currentLanguage)

  // Local draft state (editable copy of saved rules)
  const [localRules, setLocalRules] = useState<IndexRule[]>([])
  const [hasLoadedOnce, setHasLoadedOnce] = useState(false)

  // Seed local rules from server on first load
  useEffect(() => {
    if (!hasLoadedOnce && rulesQuery.data?.data?.rules) {
      const serverRules = rulesQuery.data.data.rules
      setLocalRules(serverRules.length > 0 ? serverRules : [makeEmptyRule()])
      setHasLoadedOnce(true)
    }
  }, [rulesQuery.data, hasLoadedOnce])

  const scopeOptions: ScopeOption[] = scopeQuery.data?.data || []

  // Determine if rules have unsaved changes
  const savedJson = JSON.stringify(rulesQuery.data?.data?.rules || [])
  const localJson = JSON.stringify(localRules)
  const isDirty = hasLoadedOnce && savedJson !== localJson

  // ── Handlers ──

  const addRule = () => {
    setLocalRules(prev => [...prev, makeEmptyRule()])
  }

  const removeRule = (id: string) => {
    setLocalRules(prev => {
      const next = prev.filter(r => r.id !== id)
      return next.length > 0 ? next : [makeEmptyRule()]
    })
  }

  const updateRule = (id: string, patch: Partial<IndexRule>) => {
    setLocalRules(prev => prev.map(r => r.id === id ? { ...r, ...patch } : r))
  }

  const handleSaveAndIndex = async () => {
    try {
      await saveAndIndex.mutateAsync(localRules)
    } catch (error: any) {
      alert(error?.message || t('Error starting indexing'))
    }
  }

  const isRulesLoading = rulesQuery.isLoading || scopeQuery.isLoading

  return (
    <Card>
      <CardContent className="p-0">
        {/* Section header */}
        <div className="px-5 py-4 border-b border-gray-200">
          <h2 className="text-base font-semibold text-gray-900 flex items-center gap-2">
            <Database className="w-5 h-5 text-primary-600" />
            {t('Indexing Rules')}
          </h2>
          <p className="text-xs text-gray-500 mt-1">
            {t('Define which content is included in the index — include or exclude by content type, category, tag, or individual item.')}
            <br />
            {t('A narrower "Exclude" rule always overrides a broader "Include" rule.')}
          </p>
        </div>

        {/* Rules list */}
        <div className="px-5 py-4 space-y-3">
          {isRulesLoading ? (
            <div className="text-center text-gray-500 py-8 text-sm">
              <Loader2 className="w-5 h-5 animate-spin mx-auto mb-2" />
              {t('Loading rules...')}
            </div>
          ) : (
            <>
              {localRules.map((rule) => (
                <RuleRow
                  key={rule.id}
                  rule={rule}
                  scopeOptions={scopeOptions}
                  onChange={(patch) => updateRule(rule.id, patch)}
                  onRemove={() => removeRule(rule.id)}
                  disabled={isIndexing}
                />
              ))}

              {/* Add condition button */}
              <button
                type="button"
                onClick={addRule}
                disabled={isIndexing}
                className="flex items-center gap-2 px-4 py-2.5 w-full justify-center rounded-lg border-2 border-dashed border-gray-300 text-sm text-gray-600 hover:border-primary-400 hover:text-primary-700 hover:bg-primary-50/40 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
              >
                <Plus className="w-4 h-4" />
                {t('Add Condition')}
              </button>
            </>
          )}
        </div>

        {/* ── Action bar: Save & Index + controls ── */}
        <div className="px-5 py-4 border-t border-gray-200 bg-gray-50 space-y-3">
          {/* Pricing explanation */}
          <div className="p-3 bg-purple-50/60 rounded-lg text-xs text-purple-700 space-y-1 border border-purple-100">
            <p className="font-medium text-purple-800">{t('How does the button work?')}</p>
            <p>{t('Clicking "Save & Index" saves the rules and runs an incremental deep index — unchanged content is automatically skipped, saving BoostCoin.')}</p>
          </div>

          {/* Action buttons */}
          <div className="flex flex-wrap items-center gap-2">
            {isIndexing ? (
              <>
                <Button variant="outline" onClick={paused ? resumeIndexing : pauseIndexing} loading={pausePending}>
                  {paused ? <><Play className="w-4 h-4 me-1" />{t('Resume')}</> : <><Square className="w-4 h-4 me-1" />{t('Pause')}</>}
                </Button>
                <Button variant="outline" onClick={handleStopIndexing} loading={stopPending}><Square className="w-4 h-4 me-1" />{t('Stop')}</Button>
              </>
            ) : (
              <Button onClick={handleSaveAndIndex} loading={saveAndIndex.isPending} disabled={localRules.length === 0 || isRulesLoading} className="px-6">
                <Save className="w-4 h-4 me-1" />{t('Save & Index')}
              </Button>
            )}
            <Button variant="outline" onClick={handleClearIndex} loading={clearPending} disabled={isIndexing} className="text-red-600 border-red-300 hover:bg-red-50"><Trash2 className="w-4 h-4 me-1" />{t('Delete Index')}</Button>
            {isDirty && <span className="text-xs text-amber-600 ms-auto">{t('You have unsaved changes')}</span>}
          </div>

          {/* Progress */}
          {(isIndexing || (job && job.status === 'running')) && job && (
            <div className="space-y-2 p-3 bg-blue-50 rounded-lg border border-blue-200">
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2"><Loader2 className="w-4 h-4 text-blue-600 animate-spin" /><span className="font-medium text-blue-900 text-sm">{t('Indexing in progress...')}</span></div>
                <span className="text-sm font-bold text-blue-700">{job.progress}%</span>
              </div>
              <div className="w-full bg-blue-200 rounded-full h-2.5 overflow-hidden">
                <div className="bg-blue-600 h-2.5 rounded-full transition-all duration-500" style={{ width: `${Math.max(job.progress, 2)}%` }} />
              </div>
              <div className="flex justify-between text-xs text-blue-800">
                <span>{sprintf(t('%s/%s items'), String(job.processed), String(job.total))}</span>
                {job.errors > 0 && <span className="text-amber-700">{sprintf(t('%s errors'), String(job.errors))}</span>}
                {queueStats && <span>{sprintf(t('Pending: %s'), String(queueStats.pending))}</span>}
              </div>
            </div>
          )}
        </div>
      </CardContent>
    </Card>
  )
}

// ════════════════════════════════════════════════════════════════
// Section 2: Auto Re-Index Configuration
// ════════════════════════════════════════════════════════════════

function getStaleOptions() {
  return [
    { value: 1, label: t('1 day') },
    { value: 3, label: t('3 days') },
    { value: 7, label: t('1 week') },
    { value: 14, label: t('2 weeks') },
    { value: 30, label: t('1 month') },
  ]
}

function AutoReIndexSection({ isIndexing }: { isIndexing: boolean }) {
  const { data: settingsData, isLoading: settingsLoading } = useSettings()
  const updateSettings = useUpdateSettings()
  const reindexStale = useReindexStale()

  const settings = settingsData?.data
  const autoDeepEnabled = settings?.auto_deep_index_enabled ?? true
  const staleDays = settings?.stale_index_days ?? 7

  const handleToggleAutoDeep = () => {
    updateSettings.mutate({ auto_deep_index_enabled: !autoDeepEnabled })
  }

  const handleStaleDaysChange = (days: number) => {
    updateSettings.mutate({ stale_index_days: days })
  }

  const handleReindexStale = async () => {
    if (!window.confirm(sprintf(t('Find and re-index content changed in the last %s days?'), String(staleDays)))) return
    try {
      const result = await reindexStale.mutateAsync({ days: staleDays })
      const count = result?.data?.stale_count ?? 0
      if (count === 0) {
        alert(t('All content is up to date — no re-indexing needed.'))
      } else {
        alert(sprintf(t('Found %s stale items — indexing started.'), String(count)))
      }
    } catch (error: any) {
      alert(error.message || t('Error starting re-index'))
    }
  }

  if (settingsLoading) return null

  return (
    <Card>
      <CardContent className="p-0">
        <div className="px-5 py-4 border-b border-gray-200 bg-gray-50">
          <div className="flex items-center gap-2">
            <Clock className="w-5 h-5 text-purple-600" />
            <h3 className="text-base font-semibold text-gray-900">{t('Auto Indexing')}</h3>
          </div>
          <p className="text-xs text-gray-500 mt-1">{t('Settings for automatic deep indexing of new and updated content')}</p>
        </div>

        <div className="px-5 py-4 space-y-4">
          {/* Auto deep-index toggle */}
          <div className="flex items-center justify-between p-3 rounded-lg border border-gray-200 bg-gray-50/50">
            <div className="flex-1 me-4">
              <div className="text-sm font-medium text-gray-800">{t('Auto deep index on save')}</div>
              <div className="text-xs text-gray-500 mt-0.5">{t('When a page is saved and covered by the rules — deep indexing runs automatically in the background if content or chunks are missing')}</div>
            </div>
            <button
              type="button"
              onClick={handleToggleAutoDeep}
              disabled={updateSettings.isPending}
              className="flex-shrink-0"
              aria-label={autoDeepEnabled ? t('Disable auto indexing') : t('Enable auto indexing')}
            >
              {autoDeepEnabled
                ? <ToggleRight className="w-8 h-8 text-green-500" />
                : <ToggleLeft className="w-8 h-8 text-gray-400" />
              }
            </button>
          </div>

          {/* Staleness threshold + reindex button */}
          <div className="p-3 rounded-lg border border-gray-200 bg-gray-50/50 space-y-3">
            <div className="text-sm font-medium text-gray-800">{t('Re-index stale content')}</div>
            <div className="text-xs text-gray-500">{t('Find content changed in WordPress but not updated in the index, and re-index it.')}</div>
            <div className="flex items-center gap-3 flex-wrap">
              <label className="text-xs text-gray-600 whitespace-nowrap">{t('Content changed before:')}</label>
              <select
                value={staleDays}
                onChange={e => handleStaleDaysChange(Number(e.target.value))}
                disabled={updateSettings.isPending}
                className="border rounded-lg px-3 py-1.5 text-sm bg-white min-w-[120px]"
              >
                {getStaleOptions().map(opt => (
                  <option key={opt.value} value={opt.value}>{opt.label}</option>
                ))}
              </select>
              <Button
                variant="outline"
                onClick={handleReindexStale}
                loading={reindexStale.isPending}
                disabled={isIndexing}
                className="text-purple-700 border-purple-300 hover:bg-purple-50"
              >
                <RefreshCw className="w-4 h-4 me-1" />
                {t('Re-index Stale')}
              </Button>
            </div>
          </div>
        </div>
      </CardContent>
    </Card>
  )
}

// ── Single Rule Row ─────────────────────────────────────────

function RuleRow({ rule, scopeOptions, onChange, onRemove, disabled }: {
  rule: IndexRule
  scopeOptions: ScopeOption[]
  onChange: (patch: Partial<IndexRule>) => void
  onRemove: () => void
  disabled: boolean
}) {
  // Find the current scope option to know if it needs a term selector
  const currentScope = scopeOptions.find(o => o.value === rule.scope)
  const needsTerm = currentScope?.needsTerm ?? false
  const taxonomySlug = currentScope?.taxonomy
  const needsPost = currentScope?.needsPost ?? false
  const postType = currentScope?.postType

  return (
    <div className="flex items-center gap-2 p-3 rounded-lg border border-gray-200 bg-white hover:border-gray-300 transition-colors">
      {/* 1) Include / Exclude */}
      <select
        value={rule.action}
        onChange={e => onChange({ action: e.target.value as 'include' | 'exclude' })}
        disabled={disabled}
        className={`border rounded-lg px-2 py-1.5 text-sm font-medium min-w-[100px] ${
          rule.action === 'include'
            ? 'border-green-300 bg-green-50 text-green-700'
            : 'border-red-300 bg-red-50 text-red-700'
        }`}
      >
        <option value="include">{t('Include')}</option>
        <option value="exclude">{t('Exclude')}</option>
      </select>

      {/* 2) Scope dropdown (grouped) */}
      <ScopeSelect
        value={rule.scope}
        options={scopeOptions}
        onChange={scope => {
          // When scope changes, clear dependent selectors.
          onChange({ scope, termId: null, termName: null, postId: null, postTitle: null })
        }}
        disabled={disabled}
      />

      {/* 3) Term selector (only when scope needs it) */}
      {needsTerm && taxonomySlug && (
        <TermSelector
          taxonomy={taxonomySlug}
          termId={rule.termId ?? null}
          termName={rule.termName ?? null}
          onChange={(termId, termName) => onChange({ termId, termName })}
          disabled={disabled}
        />
      )}

      {/* 4) Single-post selector (when scope targets one item) */}
      {needsPost && postType && (
        <PostSelector
          postType={postType}
          postId={rule.postId ?? null}
          postTitle={rule.postTitle ?? null}
          onChange={(postId, postTitle) => onChange({ postId, postTitle })}
          disabled={disabled}
        />
      )}

      {/* Spacer */}
      <div className="flex-1" />

      {/* Remove button */}
      <button
        type="button"
        onClick={onRemove}
        disabled={disabled}
        className="p-1.5 rounded-lg text-gray-400 hover:text-red-600 hover:bg-red-50 transition-colors disabled:opacity-40"
        title={t('Remove condition')}
      >
        <X className="w-4 h-4" />
      </button>
    </div>
  )
}

// ── Grouped Scope <select> ──────────────────────────────────

function ScopeSelect({ value, options, onChange, disabled }: {
  value: string
  options: ScopeOption[]
  onChange: (v: string) => void
  disabled: boolean
}) {
  // Build optgroups from the group field
  const groups = useMemo(() => {
    const map = new Map<string | null, ScopeOption[]>()
    for (const opt of options) {
      const key = opt.group
      if (!map.has(key)) map.set(key, [])
      map.get(key)!.push(opt)
    }
    return map
  }, [options])

  return (
    <select
      value={value}
      onChange={e => onChange(e.target.value)}
      disabled={disabled}
      className="flex-1 min-w-[160px] border border-gray-300 rounded-lg px-2 py-1.5 text-sm bg-white"
    >
      {Array.from(groups.entries()).map(([group, opts]) =>
        group === null ? (
          opts.map(o => (
            <option key={o.value} value={o.value}>{o.label}</option>
          ))
        ) : (
          <optgroup key={group} label={group}>
            {opts.map(o => (
              <option key={o.value} value={o.value}>{o.label}</option>
            ))}
          </optgroup>
        )
      )}
    </select>
  )
}

// ── Term Selector (searchable dropdown) ─────────────────────

function TermSelector({ taxonomy, termId, termName, onChange, disabled }: {
  taxonomy: string
  termId: number | null
  termName: string | null
  onChange: (termId: number | null, termName: string | null) => void
  disabled: boolean
}) {
  const termsQuery = useTaxonomyTerms(taxonomy)
  const terms: TaxonomyTerm[] = termsQuery.data?.data || []
  const [searchTerm, setSearchTerm] = useState('')
  const [isOpen, setIsOpen] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)

  // Close dropdown on outside click
  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        setIsOpen(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [])

  const filtered = useMemo(() => {
    if (!searchTerm) return terms
    const lower = searchTerm.toLowerCase()
    return terms.filter(t => t.name.toLowerCase().includes(lower) || t.slug.toLowerCase().includes(lower))
  }, [terms, searchTerm])

  const selectedLabel = termId && termName ? termName : (termId ? `#${termId}` : '')

  return (
    <div ref={containerRef} className="relative min-w-[180px]">
      {/* Display selected or search input */}
      {termId ? (
        <div className="flex items-center gap-1 border border-gray-300 rounded-lg px-2 py-1.5 text-sm bg-white">
          <span className="truncate flex-1">{selectedLabel}</span>
          <button
            type="button"
            onClick={() => { onChange(null, null); setSearchTerm('') }}
            disabled={disabled}
            className="text-gray-400 hover:text-red-500"
          >
            <X className="w-3 h-3" />
          </button>
        </div>
      ) : (
        <div className="relative">
          <Search className="absolute right-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400 pointer-events-none" />
          <input
            type="text"
            placeholder={t('Search...')}
            value={searchTerm}
            onChange={e => { setSearchTerm(e.target.value); setIsOpen(true) }}
            onFocus={() => setIsOpen(true)}
            disabled={disabled}
            className="w-full border border-gray-300 rounded-lg pe-7 ps-2 py-1.5 text-sm focus:ring-2 focus:ring-primary-500"
          />
        </div>
      )}

      {/* Dropdown */}
      {isOpen && !termId && (
        <div className="absolute z-20 mt-1 w-full max-h-48 overflow-y-auto bg-white border border-gray-200 rounded-lg shadow-lg">
          {termsQuery.isLoading ? (
            <div className="px-3 py-2 text-xs text-gray-500">{t('Loading...')}</div>
          ) : filtered.length === 0 ? (
            <div className="px-3 py-2 text-xs text-gray-400">{t('No results found')}</div>
          ) : (
            filtered.map(t => (
              <button
                key={t.id}
                type="button"
                onClick={() => { onChange(t.id, t.name); setSearchTerm(''); setIsOpen(false) }}
                className="w-full text-right px-3 py-2 text-sm hover:bg-primary-50 hover:text-primary-700 transition-colors flex items-center justify-between"
              >
                <span>{t.name}</span>
                <span className="text-[10px] text-gray-400">({t.count})</span>
              </button>
            ))
          )}
        </div>
      )}
    </div>
  )
}

// ── Post Selector (searchable dropdown for a single item) ─────────────────────
function PostSelector({ postType, postId, postTitle, onChange, disabled }: {
  postType: string
  postId: number | null
  postTitle: string | null
  onChange: (postId: number | null, postTitle: string | null) => void
  disabled: boolean
}) {
  const [searchTerm, setSearchTerm] = useState('')
  const [isOpen, setIsOpen] = useState(false)
  const containerRef = useRef<HTMLDivElement>(null)
  const postsQuery = useScopePosts(postType, searchTerm, 25)
  const posts: ScopePostItem[] = postsQuery.data?.data || []

  useEffect(() => {
    const handler = (e: MouseEvent) => {
      if (containerRef.current && !containerRef.current.contains(e.target as Node)) {
        setIsOpen(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [])

  const selectedLabel = postId && postTitle ? postTitle : (postId ? `#${postId}` : '')

  return (
    <div ref={containerRef} className="relative min-w-[220px]">
      {postId ? (
        <div className="flex items-center gap-1 border border-gray-300 rounded-lg px-2 py-1.5 text-sm bg-white">
          <span className="truncate flex-1">{selectedLabel}</span>
          <button
            type="button"
            onClick={() => { onChange(null, null); setSearchTerm('') }}
            disabled={disabled}
            className="text-gray-400 hover:text-red-500"
          >
            <X className="w-3 h-3" />
          </button>
        </div>
      ) : (
        <div className="relative">
          <Search className="absolute right-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400 pointer-events-none" />
          <input
            type="text"
            placeholder={t('Search page / post...')}
            value={searchTerm}
            onChange={e => { setSearchTerm(e.target.value); setIsOpen(true) }}
            onFocus={() => setIsOpen(true)}
            disabled={disabled}
            className="w-full border border-gray-300 rounded-lg pe-7 ps-2 py-1.5 text-sm focus:ring-2 focus:ring-primary-500"
          />
        </div>
      )}

      {isOpen && !postId && (
        <div className="absolute z-20 mt-1 w-full max-h-56 overflow-y-auto bg-white border border-gray-200 rounded-lg shadow-lg">
          {postsQuery.isLoading ? (
            <div className="px-3 py-2 text-xs text-gray-500">{t('Loading...')}</div>
          ) : posts.length === 0 ? (
            <div className="px-3 py-2 text-xs text-gray-400">{t('No results found')}</div>
          ) : (
            posts.map(p => (
              <button
                key={p.id}
                type="button"
                onClick={() => { onChange(p.id, p.title); setSearchTerm(''); setIsOpen(false) }}
                className="w-full text-right px-3 py-2 text-sm hover:bg-primary-50 hover:text-primary-700 transition-colors flex items-center justify-between gap-2"
                title={p.url}
              >
                <span className="truncate">{p.title || `#${p.id}`}</span>
                <span className="text-[10px] text-gray-400 shrink-0">#{p.id}</span>
              </button>
            ))
          )}
        </div>
      )}
    </div>
  )
}

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

let _ruleCounter = 0
function makeEmptyRule(): IndexRule {
  return {
    id: `rule-${Date.now()}-${++_ruleCounter}`,
    action: 'include',
    scope: 'all',
    termId: null,
    termName: null,
    postId: null,
    postTitle: null,
  }
}
