'use client'

import { useState, useEffect, useCallback, useMemo, type ReactNode } from 'react'
import { useRouter } from 'next/navigation'
import { useReducedMotion } from 'motion/react'
import { Inbox, Send, Bell, Mail } from 'lucide-react'
import { useLanguage } from '@/lib/i18n'
import { useAiConsent } from '@/components/legal/ai-consent-provider'
import { createNote } from '@/app/actions/notes'
import { emitNoteChange } from '@/lib/note-change-sync'
import { toast } from 'sonner'
import { IntelligenceHub } from '@/components/intelligence-hub'
import { DashboardWidgetGrid } from '@/components/dashboard-widget-grid'
import type { DashboardWidgetId } from '@/lib/dashboard/layout'
import { DashboardWidgetHelp } from '@/components/dashboard-widget-help'
import { DashboardWidgetTitleRow } from '@/components/dashboard-widget-title-row'
import { DashboardActionStrip } from '@/components/dashboard-action-strip'
import { DashboardResumeHero } from '@/components/dashboard-resume-hero'
import { DashboardMindOrbit } from '@/components/dashboard-mind-orbit'
import { DashboardAgentCarousel } from '@/components/dashboard-agent-carousel'
import { DashboardSentimentChip } from '@/components/dashboard-sentiment-chip'
import { DashboardNextPaths } from '@/components/dashboard-next-paths'
import {
  DashboardDailyReview,
  DashboardOpenLoops,
  DashboardDailyNoteWidget,
  DashboardLinkSuggestions,
  DashboardBridgesWidget,
} from '@/components/dashboard-path-widgets'
import type { DashboardPath } from '@/lib/dashboard/path-types'
import { buildFastPathsFromBriefing } from '@/lib/dashboard/paths-fast'
import { emitAiUsageChanged } from '@/lib/ai-usage-sync'
import {
  DashboardInboxWidget,
  DashboardRevisionWidget,
  DashboardStatsWidget,
  DashboardAgentActivityWidget,
  DashboardGmailWidget,
  DashboardPinnedWidget,
  DashboardActivityWidget,
  DashboardUsageWidget,
  DashboardFlashcardsProgressWidget,
} from '@/components/dashboard-catalog-widgets'
import {
  ScanEye, Search, Zap, CloudRain, Waves, HeartPulse, Lightbulb, Brain,
} from 'lucide-react'
import type { LucideIcon } from 'lucide-react'

interface BriefingPinnedNote {
  id: string
  title: string | null
  notebookId: string | null
  updatedAt: string
}

interface ActivityDay {
  date: string
  count: number
}

interface DashboardOpenLoopItem {
  id: string
  title: string | null
  notebookId: string | null
  daysStale: number
}

interface BriefingNote {
  id: string
  title: string | null
  content: string
  color: string
  notebookId: string | null
  updatedAt: string
  notebook: { id: string; name: string; color: string | null; icon: string | null } | null
}

interface BriefingInsight {
  id: string
  insight: string
  score: number
  date: string
  viewed: boolean
  note1: { id: string; title: string | null }
  note2: { id: string; title: string | null }
  note1Excerpt?: string
  note2Excerpt?: string
}

interface BridgeSuggestionItem {
  clusterAId: number
  clusterBId: number
  clusterAName: string
  clusterBName: string
  suggestedTitle: string
  suggestedContent: string
  justification: string
}

interface BriefingAiStatus {
  consent: boolean
  memoryEchoEnabled: boolean
  providerReady: boolean
  active: boolean
}

interface BriefingAgentAction {
  id: string
  agentName: string
  agentType: string
  result: string | null
  createdAt: string
}

interface AgentSuggestionItem {
  id: string
  topic: string
  reason: string
  suggestedType: string
  suggestedFrequency: string
  relatedNoteCount: number
  clusterId: number | null
}

interface BriefingReminder {
  id: string
  title: string
  reminder: string | null
  notebookId: string | null
}

interface SentimentData {
  available: boolean
  dominantEmotion?: string
  sentimentScore?: number
  emotions?: Record<string, number>
  summary?: string
  topTopic?: string
}

interface MindMapData {
  clusters: { clusterId: number; name?: string; noteIds: string[] }[]
  bridgeNotes: {
    noteId: string
    bridgeScore: number
    clusterNames?: string[]
    note?: { id: string; title: string | null; content?: string }
  }[]
  cached: boolean
  totalNotes: number
}

interface DashboardViewProps {
  onNoteSelect: (noteId: string, notebookId: string | null) => void
}

interface GmailStatus {
  connected: boolean
  recentCaptures: number
}

const EMOTION_META: Record<string, { Icon: LucideIcon; color: string }> = {
  focused: { Icon: ScanEye, color: '#D97706' },
  curious: { Icon: Search, color: '#0891B2' },
  enthusiastic: { Icon: Zap, color: '#EA580C' },
  frustrated: { Icon: CloudRain, color: '#DC2626' },
  calm: { Icon: Waves, color: '#059669' },
  anxious: { Icon: HeartPulse, color: '#7C3AED' },
  creative: { Icon: Lightbulb, color: '#DB2777' },
  reflective: { Icon: Brain, color: '#4F46E5' },
}

function formatRelativeTime(
  dateStr: string,
  t: (key: string, params?: Record<string, string | number>) => string,
): string {
  const diff = Date.now() - new Date(dateStr).getTime()
  const mins = Math.floor(diff / 60000)
  const hours = Math.floor(diff / 3600000)
  const days = Math.floor(diff / 86400000)
  if (mins < 1) return t('time.justNow')
  if (mins < 60) return t('time.minutesAgo', { count: mins })
  if (hours < 24) return t('time.hoursAgo', { count: hours })
  return t('time.daysAgo', { count: days })
}

function localeForLanguage(language: string): string {
  const map: Record<string, string> = {
    en: 'en-US', fr: 'fr-FR', es: 'es-ES', de: 'de-DE', it: 'it-IT',
    pt: 'pt-PT', nl: 'nl-NL', pl: 'pl-PL', ru: 'ru-RU', zh: 'zh-CN',
    ja: 'ja-JP', ko: 'ko-KR', ar: 'ar-SA', fa: 'fa-IR', hi: 'hi-IN',
  }
  return map[language] || language
}

function stripHtml(html: string | null | undefined): string {
  if (!html) return ''
  return html.replace(/<[^>]+>/g, ' ').replace(/&nbsp;/g, ' ').replace(/\s+/g, ' ').trim()
}

// ─── Main ─────────────────────────────────────────────

export function DashboardView({ onNoteSelect }: DashboardViewProps) {
  const router = useRouter()
  const { t, language } = useLanguage()
  const { hasAiConsent, requestAiConsent } = useAiConsent()
  const prefersReducedMotion = useReducedMotion()

  const scrollToWidget = (id: string) => {
    document.getElementById(`dashboard-widget-${id}`)?.scrollIntoView({
      behavior: prefersReducedMotion ? 'auto' : 'smooth',
      block: 'start',
    })
  }

  const [data, setData] = useState<{
    recentNotes: BriefingNote[]
    inboxCount: number
    dueFlashcards: number
    upcomingReminders: BriefingReminder[]
    insights: BriefingInsight[]
    agentActions: BriefingAgentAction[]
    agentSuggestions?: AgentSuggestionItem[]
    bridgeSuggestions?: BridgeSuggestionItem[]
    gmail?: GmailStatus
    ai?: BriefingAiStatus
    pinnedNotes?: BriefingPinnedNote[]
    writingActivity?: ActivityDay[]
  } | null>(null)
  const [paths, setPaths] = useState<DashboardPath[]>([])
  const [openLoops, setOpenLoops] = useState<DashboardOpenLoopItem[]>([])
  const [pathsEnriching, setPathsEnriching] = useState(false)
  const [flashcardStats, setFlashcardStats] = useState<{
    retentionRate: number
    streak: number
    totalCards: number
  } | null>(null)
  const [sentiment, setSentiment] = useState<SentimentData | null>(null)
  const [mindMap, setMindMap] = useState<MindMapData | null>(null)
  const [mindMapLoading, setMindMapLoading] = useState(true)
  const [sentimentLoading, setSentimentLoading] = useState(true)
  const [captureText, setCaptureText] = useState('')
  const [capturing, setCapturing] = useState(false)
  const [inboxPulse, setInboxPulse] = useState(0)
  const [actingSuggestionId, setActingSuggestionId] = useState<string | null>(null)
  const [echoRefreshing, setEchoRefreshing] = useState(false)
  const [dismissingInsightId, setDismissingInsightId] = useState<string | null>(null)
  const [actingBridgeSuggestionKey, setActingBridgeSuggestionKey] = useState<string | null>(null)
  const [briefingError, setBriefingError] = useState(false)

  const loadBriefing = useCallback(async () => {
    try {
      const res = await fetch('/api/briefing', { cache: 'no-store' })
      if (res.ok) {
        setData(await res.json())
        setBriefingError(false)
      } else {
        setBriefingError(true)
      }
    } catch {
      setBriefingError(true)
    }
  }, [])

  const loadPaths = useCallback(async (briefing: NonNullable<typeof data>) => {
    setPathsEnriching(true)
    try {
      const focus = briefing.recentNotes[0]
      const res = await fetch('/api/briefing/paths', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          recentNote: focus
            ? {
                id: focus.id,
                title: focus.title,
                content: focus.content,
                notebookId: focus.notebookId,
              }
            : null,
          inboxCount: briefing.inboxCount,
          dueFlashcards: briefing.dueFlashcards,
          insights: briefing.insights,
          bridgeSuggestions: briefing.bridgeSuggestions ?? [],
          agentSuggestions: briefing.agentSuggestions ?? [],
        }),
        cache: 'no-store',
      })
      if (res.ok) {
        const json = await res.json()
        setPaths(json.paths ?? [])
        setOpenLoops(json.openLoops ?? [])
      }
    } catch {
      /* pistes enrichies optionnelles — les pistes rapides restent affichées */
    } finally {
      setPathsEnriching(false)
    }
  }, [])

  /** Recharge briefing puis enrichit les pistes (évite loadPaths() sans argument). */
  const reloadBriefingAndPaths = useCallback(async () => {
    try {
      const res = await fetch('/api/briefing', { cache: 'no-store' })
      if (!res.ok) {
        setBriefingError(true)
        return
      }
      const briefing = await res.json()
      setData(briefing)
      setBriefingError(false)
      setPaths(buildFastPathsFromBriefing({
        recentNotes: (briefing.recentNotes ?? []).map((n: BriefingNote) => ({
          id: n.id,
          title: n.title,
          content: n.content,
          notebookId: n.notebookId,
        })),
        inboxCount: briefing.inboxCount ?? 0,
        dueFlashcards: briefing.dueFlashcards ?? 0,
        insights: briefing.insights ?? [],
        bridgeSuggestions: briefing.bridgeSuggestions ?? [],
        agentSuggestions: briefing.agentSuggestions ?? [],
      }))
      await loadPaths(briefing)
    } catch {
      setBriefingError(true)
    }
  }, [loadPaths])

  const loadSentiment = useCallback(async () => {
    try {
      const res = await fetch('/api/briefing/sentiment', { cache: 'no-store' })
      if (res.ok) {
        setSentiment(await res.json())
        emitAiUsageChanged()
      }
    } catch {
      /* widget sentiment en état vide */
    } finally {
      setSentimentLoading(false)
    }
  }, [])

  const loadMindMap = useCallback(async () => {
    try {
      const res = await fetch('/api/clusters?lite=1', { cache: 'no-store' })
      if (res.ok) {
        const json = await res.json()
        setMindMap({
          clusters: json.clusters || [],
          bridgeNotes: json.bridgeNotes || [],
          cached: !!json.cached,
          totalNotes: json.totalNotes || 0,
        })
      }
    } catch {} finally { setMindMapLoading(false) }
  }, [])

  useEffect(() => {
    loadBriefing()
    loadMindMap()
    fetch('/api/flashcards/stats', { cache: 'no-store' })
      .then(r => r.ok ? r.json() : null)
      .then(json => {
        setFlashcardStats({
          retentionRate: json?.retentionRate ?? 0,
          streak: json?.streak ?? 0,
          totalCards: json?.totalCards ?? 0,
        })
      })
      .catch(() => {
        setFlashcardStats({ retentionRate: 0, streak: 0, totalCards: 0 })
      })
  }, [loadBriefing, loadMindMap])

  // Pistes rapides dès le briefing, puis enrichissement en arrière-plan
  useEffect(() => {
    if (!data) return
    setPaths(buildFastPathsFromBriefing({
      recentNotes: data.recentNotes.map(n => ({
        id: n.id,
        title: n.title,
        content: n.content,
        notebookId: n.notebookId,
      })),
      inboxCount: data.inboxCount,
      dueFlashcards: data.dueFlashcards,
      insights: data.insights,
      bridgeSuggestions: data.bridgeSuggestions ?? [],
      agentSuggestions: data.agentSuggestions ?? [],
    }))
    void loadPaths(data)
  }, [data, loadPaths])

  // Sentiment (LLM) en arrière-plan — ne bloque pas le reste du tableau de bord
  useEffect(() => {
    const run = () => { void loadSentiment() }
    if (typeof requestIdleCallback === 'function') {
      const id = requestIdleCallback(run, { timeout: 2500 })
      return () => cancelIdleCallback(id)
    }
    const t = setTimeout(run, 400)
    return () => clearTimeout(t)
  }, [loadSentiment])

  const aiStatus = data?.ai
  const aiActive = aiStatus?.active ?? false
  const inboxCount = data?.inboxCount ?? 0
  const dueFlashcards = data?.dueFlashcards ?? 0
  const reminders = data?.upcomingReminders ?? []
  const insights = data?.insights ?? []
  const agentActions = data?.agentActions ?? []
  const agentSuggestions = data?.agentSuggestions ?? []
  const bridgeSuggestions = data?.bridgeSuggestions ?? []
  const gmail = data?.gmail
  const pinnedNotes = data?.pinnedNotes ?? []
  const writingActivity = data?.writingActivity ?? []
  const pathsList = paths
  const openLoopsList = openLoops
  const briefingLoading = data === null
  const pathsLoading = data === null
  const pathsDetailLoading = pathsEnriching
  const recentNotes = data?.recentNotes ?? []
  const topBridgeNotes = useMemo(() => (mindMap?.bridgeNotes ?? []).slice(0, 3), [mindMap?.bridgeNotes])
  const dateLocale = localeForLanguage(language)

  const discoveryCount = useMemo(() => {
    const fresh = insights.filter(i => !i.viewed).length
    return fresh + bridgeSuggestions.length + agentActions.length
  }, [insights, bridgeSuggestions, agentActions])

  const themeCount = mindMap?.clusters.length ?? 0

  const resumeNotes = useMemo(() => recentNotes.map(n => ({
    id: n.id,
    title: n.title,
    excerpt: stripHtml(n.content).slice(0, 180),
    notebookName: n.notebook?.name || t('homeDashboard.inbox'),
    notebookColor: n.notebook?.color || '#8B5CF6',
    updatedAt: n.updatedAt,
    notebookId: n.notebookId,
  })), [recentNotes, t])

  const briefingSubtitle = useMemo(() => {
    if (briefingLoading) return ''
    const parts: string[] = []
    if (inboxCount > 0) parts.push(t('homeDashboard.pulseInbox', { count: inboxCount }))
    if (dueFlashcards > 0) parts.push(t('homeDashboard.pulseReview', { count: dueFlashcards }))
    if (discoveryCount > 0) parts.push(t('homeDashboard.pulseDiscoveries', { count: discoveryCount }))
    if (parts.length === 0) return t('homeDashboard.pulseClear')
    return parts.join(' · ')
  }, [briefingLoading, inboxCount, dueFlashcards, discoveryCount, t])

  const handleCapture = useCallback(async () => {
    const text = captureText.trim()
    if (!text) return
    setCapturing(true)
    try {
      const words = text.split(/\s+/)
      const title = words.slice(0, 5).join(' ') + (words.length > 5 ? '…' : '')
      const note = await createNote({ title, content: `<p>${text.replace(/</g, '&lt;')}</p>` })
      if (note) {
        emitNoteChange({ type: 'created', note })
        setCaptureText('')
        setInboxPulse(p => p + 1)
        setData(prev => prev ? { ...prev, inboxCount: prev.inboxCount + 1 } : prev)
        toast.success(t('homeDashboard.captured'), { duration: 2000 })
      }
    } catch {
      toast.error(t('homeDashboard.captureError'))
    } finally {
      setCapturing(false)
    }
  }, [captureText, t])

  const handleCaptureKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleCapture() }
  }

  const markInsightViewed = useCallback(async (insightId: string) => {
    await fetch('/api/ai/echo', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ action: 'view', insightId }),
    }).catch(() => {})
    setData(prev => prev ? {
      ...prev,
      insights: prev.insights.map(i => i.id === insightId ? { ...i, viewed: true } : i),
    } : prev)
  }, [])

  const handleOpenFromInsight = useCallback(async (insight: BriefingInsight, noteId: string) => {
    await markInsightViewed(insight.id)
    onNoteSelect(noteId, null)
  }, [markInsightViewed, onNoteSelect])

  const handleDismissInsight = useCallback(async (insight: BriefingInsight) => {
    setDismissingInsightId(insight.id)
    try {
      const res = await fetch('/api/ai/echo/dismiss', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ noteId: insight.note1.id, connectedNoteId: insight.note2.id }),
      })
      if (!res.ok) throw new Error()
      setData(prev => prev ? { ...prev, insights: prev.insights.filter(i => i.id !== insight.id) } : prev)
    } catch {
      toast.error(t('homeDashboard.captureError'))
    } finally {
      setDismissingInsightId(null)
    }
  }, [t])

  const handleRefreshEcho = useCallback(async () => {
    const consented = await requestAiConsent()
    if (!consented) return
    setEchoRefreshing(true)
    try {
      const res = await fetch('/api/ai/echo')
      const json = await res.json()
      if (res.status === 403 && json.error === 'ai_consent_required') { await requestAiConsent(); return }
      if (!res.ok) throw new Error(json.error)
      if (json.insight) {
        await reloadBriefingAndPaths()
        emitAiUsageChanged()
        toast.success(t('homeDashboard.echoFound'))
      } else {
        toast.info(t('homeDashboard.echoNone'))
      }
    } catch {
      toast.error(t('homeDashboard.echoFailed'))
    } finally {
      setEchoRefreshing(false)
    }
  }, [requestAiConsent, reloadBriefingAndPaths, t])

  const handleEnableAi = useCallback(async () => {
    await requestAiConsent()
    await reloadBriefingAndPaths()
  }, [requestAiConsent, reloadBriefingAndPaths])

  const handleDismissBridgeSuggestion = useCallback(async (s: BridgeSuggestionItem) => {
    const key = `${s.clusterAId}-${s.clusterBId}`
    setActingBridgeSuggestionKey(key)
    try {
      const res = await fetch('/api/bridge-notes', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ clusterAId: s.clusterAId, clusterBId: s.clusterBId }),
      })
      if (!res.ok) throw new Error()
      setData(prev => prev ? {
        ...prev,
        bridgeSuggestions: prev.bridgeSuggestions?.filter(
          x => !(x.clusterAId === s.clusterAId && x.clusterBId === s.clusterBId),
        ) ?? [],
      } : prev)
    } catch {
      toast.error(t('homeDashboard.captureError'))
    } finally {
      setActingBridgeSuggestionKey(null)
    }
  }, [t])

  const handleCreateBridgeSuggestion = useCallback(async (s: BridgeSuggestionItem) => {
    const key = `${s.clusterAId}-${s.clusterBId}`
    setActingBridgeSuggestionKey(key)
    try {
      const html = `<p>${s.suggestedContent.replace(/</g, '&lt;')}</p>`
      const note = await createNote({ title: s.suggestedTitle, content: html })
      if (!note) throw new Error()
      emitNoteChange({ type: 'created', note })
      await fetch('/api/bridge-notes', {
        method: 'DELETE',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ clusterAId: s.clusterAId, clusterBId: s.clusterBId }),
      }).catch(() => {})
      setData(prev => prev ? {
        ...prev,
        bridgeSuggestions: prev.bridgeSuggestions?.filter(
          x => !(x.clusterAId === s.clusterAId && x.clusterBId === s.clusterBId),
        ) ?? [],
      } : prev)
      toast.success(t('homeDashboard.bridgeNoteCreated'))
      onNoteSelect(note.id, note.notebookId ?? null)
    } catch {
      toast.error(t('homeDashboard.captureError'))
    } finally {
      setActingBridgeSuggestionKey(null)
    }
  }, [onNoteSelect, t])

  const formatAgentFrequency = useCallback((frequency: string) => {
    const key = `agents.frequencies.${frequency.toLowerCase()}`
    const label = t(key)
    return label !== key ? label : frequency
  }, [t])

  const handleAcceptSuggestion = useCallback(async (id: string) => {
    setActingSuggestionId(id)
    try {
      const res = await fetch(`/api/agents/suggestions/${id}/accept`, { method: 'POST' })
      const json = await res.json()
      if (!res.ok) throw new Error(json.error)
      setData(prev => prev ? { ...prev, agentSuggestions: prev.agentSuggestions?.filter(s => s.id !== id) ?? [] } : prev)
      toast.success(t('homeDashboard.agentCreated'))
      if (json.agentId) router.push(`/agents?id=${json.agentId}`)
    } catch {
      toast.error(t('homeDashboard.agentFailed'))
    } finally {
      setActingSuggestionId(null)
    }
  }, [t, router])

  const handleDismissSuggestion = useCallback(async (id: string) => {
    setActingSuggestionId(id)
    try {
      const res = await fetch(`/api/agents/suggestions/${id}/dismiss`, { method: 'POST' })
      if (!res.ok) throw new Error()
      setData(prev => prev ? { ...prev, agentSuggestions: prev.agentSuggestions?.filter(s => s.id !== id) ?? [] } : prev)
    } catch {
      toast.error(t('homeDashboard.captureError'))
    } finally {
      setActingSuggestionId(null)
    }
  }, [t])

  const handlePathAction = useCallback((path: DashboardPath) => {
    switch (path.actionKey) {
      case 'continue':
        if (path.noteId) onNoteSelect(path.noteId, path.notebookId ?? null)
        break
      case 'compare':
        if (path.noteId) onNoteSelect(path.noteId, path.notebookId ?? null)
        if (path.note2Id) toast.info(t('homeDashboard.pathCompareHint', { title: path.title }))
        break
      case 'addLink':
        if (path.noteId) {
          onNoteSelect(path.noteId, path.notebookId ?? null)
          toast.info(t('homeDashboard.pathAddLinkHint', { link: path.title }))
        }
        break
      case 'openInsight': {
        const insight = insights.find(i => i.id === path.insightId)
        if (insight && path.noteId) handleOpenFromInsight(insight, path.noteId)
        break
      }
      case 'createBridge': {
        const bridge = bridgeSuggestions.find(
          b => b.clusterAId === path.clusterAId && b.clusterBId === path.clusterBId,
        )
        if (bridge) handleCreateBridgeSuggestion(bridge)
        break
      }
      case 'createAgent':
        if (path.agentSuggestionId) handleAcceptSuggestion(path.agentSuggestionId)
        break
      case 'exploreTheme':
        router.push('/insights')
        break
      case 'organizeInbox':
        router.push('/home?forceList=1')
        break
      case 'reviewCards':
        router.push('/revision')
        break
      case 'openDaily':
        fetch('/api/notes/daily')
          .then(r => r.ok ? r.json() : null)
          .then(json => {
            if (json?.note?.id) onNoteSelect(json.note.id, json.note.notebookId ?? null)
          })
          .catch(() => toast.error(t('homeDashboard.captureError')))
        break
      default:
        break
    }
  }, [
    onNoteSelect, t, insights, handleOpenFromInsight, bridgeSuggestions,
    handleCreateBridgeSuggestion, handleAcceptSuggestion, router,
  ])

  const handleOpenDailyNote = useCallback(() => {
    fetch('/api/notes/daily')
      .then(r => r.ok ? r.json() : null)
      .then(json => {
        if (json?.note?.id) onNoteSelect(json.note.id, json.note.notebookId ?? null)
      })
      .catch(() => toast.error(t('homeDashboard.captureError')))
  }, [onNoteSelect, t])

  const dailyReviewItems = useMemo(() => [
    {
      key: 'inbox',
      label: t('homeDashboard.dailyReviewInbox'),
      done: inboxCount === 0,
      count: inboxCount,
      onClick: () => router.push('/home?forceList=1'),
    },
    {
      key: 'discoveries',
      label: t('homeDashboard.dailyReviewDiscoveries'),
      done: discoveryCount === 0,
      count: discoveryCount,
      onClick: () => scrollToWidget('intelligence'),
    },
    {
      key: 'connection',
      label: t('homeDashboard.dailyReviewConnection'),
      // Coché quand il n'y a plus de piste de connexion à traiter
      done: !pathsList.some(p => p.type === 'connect' || p.type === 'resurface'),
      onClick: () => {
        const p = pathsList.find(x => x.type === 'connect' || x.type === 'resurface')
        if (p) handlePathAction(p)
        else scrollToWidget('next-paths')
      },
    },
    {
      key: 'review',
      label: t('homeDashboard.dailyReviewCards'),
      done: dueFlashcards === 0,
      count: dueFlashcards,
      onClick: () => router.push('/revision'),
    },
  ], [t, inboxCount, discoveryCount, dueFlashcards, pathsList, router, handlePathAction])

  const linkSuggestionPaths = useMemo(
    () => pathsList.filter(p => p.type === 'add-link').map(p => ({
      id: p.id,
      title: p.title,
      description: p.description,
      score: p.score,
    })),
    [pathsList],
  )

  const relTime = useCallback((d: string) => formatRelativeTime(d, t), [t])

  const renderWidget = useCallback((id: DashboardWidgetId) => {
    const wrap = (node: ReactNode) => (
      <div id={`dashboard-widget-${id}`}>{node}</div>
    )

    switch (id) {
      case 'capture':
        return wrap(
          <div className="relative rounded-xl border border-border/30 bg-white/80 dark:bg-zinc-900/80 backdrop-blur-sm shadow-sm h-full">
            <div className="flex items-center justify-between gap-2 px-3.5 pt-2.5">
              <div className="flex items-center gap-2 min-w-0">
                <Inbox size={11} className="text-brand-accent shrink-0" />
                <span className="text-[8px] font-mono font-bold uppercase tracking-widest text-concrete">
                  {t('homeDashboard.quickCapture')}
                </span>
              </div>
              <DashboardWidgetHelp widgetId="capture" />
            </div>
            <textarea
              value={captureText}
              onChange={e => setCaptureText(e.target.value)}
              onKeyDown={handleCaptureKeyDown}
              placeholder={t('homeDashboard.quickCapturePlaceholder')}
              rows={2}
              className="w-full text-sm px-3.5 pb-3 pt-1.5 pe-12 bg-transparent outline-none text-ink dark:text-dark-ink resize-none leading-relaxed placeholder:text-concrete/45"
            />
            <button
              type="button"
              onClick={handleCapture}
              disabled={!captureText.trim() || capturing}
              className="absolute bottom-2.5 end-2.5 p-2 bg-ink text-white dark:bg-white dark:text-black rounded-lg disabled:opacity-25 hover:scale-105 active:scale-95 transition-all shadow-sm"
            >
              <Send size={12} />
            </button>
          </div>,
        )
      case 'next-paths':
        return wrap(
          <DashboardNextPaths
            paths={pathsList}
            loading={pathsLoading}
            enriching={pathsEnriching}
            focusNoteTitle={recentNotes[0]?.title}
            onAction={handlePathAction}
            prefersReducedMotion={!!prefersReducedMotion}
          />,
        )
      case 'daily-review':
        return wrap(
          <DashboardDailyReview items={dailyReviewItems} loading={briefingLoading} />,
        )
      case 'open-loops':
        return wrap(
          <DashboardOpenLoops loops={openLoopsList} loading={pathsDetailLoading && openLoopsList.length === 0} onSelect={onNoteSelect} />,
        )
      case 'daily-note':
        return wrap(
          <DashboardDailyNoteWidget loading={briefingLoading} onOpen={handleOpenDailyNote} />,
        )
      case 'link-suggestions':
        return wrap(
          <DashboardLinkSuggestions
            paths={linkSuggestionPaths}
            loading={pathsDetailLoading && linkSuggestionPaths.length === 0}
            onAction={(id) => {
              const p = pathsList.find(x => x.id === id)
              if (p) handlePathAction(p)
            }}
          />,
        )
      case 'bridges':
        return wrap(
          <DashboardBridgesWidget
            suggestions={(bridgeSuggestions ?? []).map(s => ({
              clusterAId: s.clusterAId,
              clusterBId: s.clusterBId,
              clusterAName: s.clusterAName,
              clusterBName: s.clusterBName,
              suggestedTitle: s.suggestedTitle,
              justification: s.justification,
            }))}
            loading={briefingLoading}
            actingKey={actingBridgeSuggestionKey}
            onCreate={handleCreateBridgeSuggestion}
            onDismiss={(s) => handleDismissBridgeSuggestion(
              bridgeSuggestions.find(b => b.clusterAId === s.clusterAId && b.clusterBId === s.clusterBId)!,
            )}
          />,
        )
      case 'flashcards-progress':
        return wrap(
          <DashboardFlashcardsProgressWidget
            retentionRate={flashcardStats?.retentionRate ?? 0}
            streak={flashcardStats?.streak ?? 0}
            totalCards={flashcardStats?.totalCards ?? 0}
            dueCount={dueFlashcards}
            loading={!flashcardStats}
            onOpen={() => router.push('/revision')}
          />,
        )
      case 'resume':
        return wrap(
          <DashboardResumeHero
            notes={resumeNotes}
            loading={briefingLoading}
            onSelect={onNoteSelect}
            onCaptureFocus={() => {
              document.getElementById('dashboard-widget-capture')?.scrollIntoView({
                behavior: prefersReducedMotion ? 'auto' : 'smooth',
                block: 'center',
              })
              const ta = document.querySelector<HTMLTextAreaElement>('#dashboard-widget-capture textarea')
              ta?.focus()
            }}
            formatRelativeTime={relTime}
            prefersReducedMotion={!!prefersReducedMotion}
          />,
        )
      case 'intelligence':
        return wrap(
          <IntelligenceHub
            loading={briefingLoading}
            aiActive={aiActive}
            hasAiConsent={hasAiConsent}
            providerReady={aiStatus?.providerReady ?? false}
            memoryEchoEnabled={aiStatus?.memoryEchoEnabled ?? true}
            insights={insights}
            bridgeNotes={topBridgeNotes}
            bridgeSuggestions={bridgeSuggestions}
            agentActions={agentActions}
            onNoteSelect={(nid) => onNoteSelect(nid, null)}
            onRefreshEcho={handleRefreshEcho}
            onEnableAi={handleEnableAi}
            echoRefreshing={echoRefreshing}
            onDismissInsight={handleDismissInsight}
            onDismissBridgeSuggestion={handleDismissBridgeSuggestion}
            onCreateBridgeSuggestion={handleCreateBridgeSuggestion}
            onOpenInsightNote={handleOpenFromInsight}
            dismissingInsightId={dismissingInsightId}
            actingBridgeSuggestionKey={actingBridgeSuggestionKey}
            prefersReducedMotion={!!prefersReducedMotion}
          />,
        )
      case 'reminders':
        return wrap(
          <div className="rounded-2xl border border-border/30 bg-white dark:bg-zinc-900 p-4">
            <DashboardWidgetTitleRow
              widgetId="reminders"
              icon={<Bell size={12} className="text-brand-accent" />}
              title={t('homeDashboard.reminders')}
            />
            {briefingLoading ? (
              <div className="space-y-2">
                <div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
                <div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
              </div>
            ) : reminders.length === 0 ? (
              <p className="text-[11px] text-concrete italic py-1">{t('homeDashboard.allCaughtUp')}</p>
            ) : (
              <div className="space-y-1.5">
                {reminders.slice(0, 4).map(r => (
                  <button
                    key={r.id}
                    type="button"
                    onClick={() => onNoteSelect(r.id, r.notebookId)}
                    className="w-full flex items-center justify-between gap-2 p-2.5 rounded-xl border border-border/20 hover:border-brand-accent/25 hover:bg-brand-accent/[0.03] transition-all text-start group"
                  >
                    <span className="text-[11px] text-ink dark:text-dark-ink group-hover:text-brand-accent transition-colors truncate flex-1">
                      {r.title || t('homeDashboard.untitled')}
                    </span>
                    <span className="text-[8px] font-mono font-bold text-brand-accent bg-brand-accent/10 px-1.5 py-0.5 rounded shrink-0">
                      {r.reminder ? new Date(r.reminder).toLocaleDateString(dateLocale, { day: 'numeric', month: 'short' }) : ''}
                    </span>
                  </button>
                ))}
              </div>
            )}
            {!briefingLoading && (
              gmail?.connected ? (
                <button
                  type="button"
                  onClick={() => router.push('/settings/integrations')}
                  className="w-full flex items-center justify-between gap-2 p-2.5 rounded-xl border border-border/20 hover:border-brand-accent/25 transition-all text-start mt-2"
                >
                  <div className="flex items-center gap-2 min-w-0">
                    <Mail size={11} className="text-concrete" />
                    <span className="text-[10px] text-ink dark:text-dark-ink truncate">{t('homeDashboard.gmailCaptures')}</span>
                  </div>
                  <span className="text-[8px] font-mono font-bold text-brand-accent bg-brand-accent/10 px-1.5 py-0.5 rounded">
                    {t('homeDashboard.gmailRecent', { count: gmail.recentCaptures })}
                  </span>
                </button>
              ) : (
                <button
                  type="button"
                  onClick={() => router.push('/settings/integrations')}
                  className="w-full text-[9px] font-mono uppercase tracking-wider text-concrete hover:text-brand-accent transition-colors text-start py-2 mt-2"
                >
                  {t('homeDashboard.gmailConnect')} →
                </button>
              )
            )}
          </div>,
        )
      case 'mind-map':
        return wrap(
          <DashboardMindOrbit
            clusters={mindMap?.clusters ?? []}
            bridgeNotes={mindMap?.bridgeNotes ?? []}
            loading={mindMapLoading}
            onOpenInsights={() => router.push('/insights')}
            onNoteSelect={(nid) => onNoteSelect(nid, null)}
            prefersReducedMotion={!!prefersReducedMotion}
          />,
        )
      case 'agents':
        return wrap(
          <DashboardAgentCarousel
            suggestions={agentSuggestions}
            loading={briefingLoading}
            actingId={actingSuggestionId}
            formatFrequency={formatAgentFrequency}
            onAccept={handleAcceptSuggestion}
            onDismiss={handleDismissSuggestion}
            prefersReducedMotion={!!prefersReducedMotion}
          />,
        )
      case 'sentiment':
        return wrap(
          <DashboardSentimentChip
            available={!!sentiment?.available}
            loading={sentimentLoading}
            dominantEmotion={sentiment?.dominantEmotion}
            summary={sentiment?.summary}
            emotions={sentiment?.emotions}
            emotionMeta={EMOTION_META}
          />,
        )
      case 'inbox':
        return wrap(
          <DashboardInboxWidget
            count={inboxCount}
            loading={briefingLoading}
            onOpen={() => router.push('/home?forceList=1')}
          />,
        )
      case 'revision':
        return wrap(
          <DashboardRevisionWidget
            dueCount={dueFlashcards}
            loading={briefingLoading}
            onOpen={() => router.push('/revision')}
          />,
        )
      case 'stats':
        return wrap(
          <DashboardStatsWidget
            clusterCount={themeCount}
            bridgeCount={mindMap?.bridgeNotes.length ?? 0}
            noteCount={mindMap?.totalNotes ?? 0}
            loading={mindMapLoading}
            onOpen={() => router.push('/insights')}
          />,
        )
      case 'agent-activity':
        return wrap(
          <DashboardAgentActivityWidget
            actions={agentActions}
            loading={briefingLoading}
            onOpen={() => router.push('/agents')}
          />,
        )
      case 'gmail':
        return wrap(
          <DashboardGmailWidget
            connected={!!gmail?.connected}
            recentCaptures={gmail?.recentCaptures ?? 0}
            loading={briefingLoading}
            onOpen={() => router.push('/settings/integrations')}
          />,
        )
      case 'activity':
        return wrap(
          <DashboardActivityWidget
            data={writingActivity}
            loading={briefingLoading}
          />,
        )
      case 'pinned':
        return wrap(
          <DashboardPinnedWidget
            notes={pinnedNotes}
            loading={briefingLoading}
            onSelect={onNoteSelect}
          />,
        )
      case 'usage':
        return wrap(
          <DashboardUsageWidget />,
        )
      default:
        return null
    }
  }, [
    t, captureText, capturing, handleCaptureKeyDown, handleCapture, resumeNotes, briefingLoading, pathsLoading, pathsEnriching, sentimentLoading,
    onNoteSelect, relTime, prefersReducedMotion, mindMapLoading, aiActive, hasAiConsent,
    aiStatus, insights, topBridgeNotes, bridgeSuggestions, agentActions, handleRefreshEcho,
    handleEnableAi, echoRefreshing, handleDismissInsight, handleDismissBridgeSuggestion,
    handleCreateBridgeSuggestion, handleOpenFromInsight, dismissingInsightId,
    actingBridgeSuggestionKey, reminders, gmail, dateLocale, router, mindMap,
    agentSuggestions, actingSuggestionId, formatAgentFrequency, handleAcceptSuggestion,
    handleDismissSuggestion, sentiment, pinnedNotes, writingActivity, themeCount,
    pathsList, openLoopsList, handlePathAction, dailyReviewItems, linkSuggestionPaths,
    handleOpenDailyNote, flashcardStats,
  ])

  return (
    <div className="h-full w-full bg-[#F9F8F6] dark:bg-[#0D0D0D] overflow-y-auto custom-scrollbar relative">
      <div className="absolute inset-0 bg-[linear-gradient(to_right,#80808005_1px,transparent_1px),linear-gradient(to_bottom,#80808005_1px,transparent_1px)] bg-[size:28px_28px] pointer-events-none z-0" />

      <div className="w-full px-4 sm:px-6 lg:px-8 xl:px-10 2xl:px-12 py-6 sm:py-8 relative z-10">
        {briefingError && !data && (
          <div className="mb-4 rounded-xl border border-rose-500/30 bg-rose-500/5 px-4 py-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
            <p className="text-sm text-ink dark:text-dark-ink">
              {t('homeDashboard.briefingLoadError')}
            </p>
            <button
              type="button"
              onClick={() => { void reloadBriefingAndPaths() }}
              className="shrink-0 text-[10px] font-mono font-bold uppercase tracking-wider px-3 py-2 rounded-lg bg-ink text-white dark:bg-white dark:text-black"
            >
              {t('homeDashboard.briefingRetry')}
            </button>
          </div>
        )}
        {/* ── En-tête : orientation en 2 secondes ── */}
        <header className="mb-5">
          <div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-2 pb-4 border-b border-border/20">
            <div>
              <h1 className="font-serif text-2xl sm:text-3xl font-medium text-ink dark:text-dark-ink tracking-tight">
                {t('homeDashboard.title')}
              </h1>
              <p className="text-[10px] font-mono uppercase tracking-[0.2em] text-concrete font-bold mt-1">
                {new Date().toLocaleDateString(dateLocale, { weekday: 'long', day: 'numeric', month: 'long' })}
              </p>
            </div>
            {!briefingLoading && briefingSubtitle && (
              <p className="text-[11px] text-concrete leading-relaxed max-w-md sm:text-end">
                {briefingSubtitle}
              </p>
            )}
          </div>

          {/* Pulse : file d'attente cognitive en un coup d'œil */}
          <div className="mt-4">
            <DashboardActionStrip
              inboxCount={inboxCount}
              dueFlashcards={dueFlashcards}
              reminderCount={reminders.length}
              discoveryCount={discoveryCount}
              themeCount={themeCount}
              inboxPulse={inboxPulse}
              onInbox={() => router.push('/home?forceList=1')}
              onReview={() => router.push('/revision')}
              onReminders={() => scrollToWidget('reminders')}
              onDiscoveries={() => scrollToWidget('intelligence')}
              onThemes={() => router.push('/insights')}
              prefersReducedMotion={!!prefersReducedMotion}
            />
          </div>
        </header>

        <div className="pb-24">
          <DashboardWidgetGrid renderWidget={renderWidget} />
        </div>
      </div>
    </div>
  )
}
