'use client'

import { createContext, useContext, useState, useEffect, useMemo, useCallback } from 'react'
import { useQueryClient } from '@tanstack/react-query'
import type { Notebook, Label, Note } from '@/lib/types'
import { LabelColorName, LABEL_COLORS } from '@/lib/types'
import { getHashColor } from '@/lib/utils'
import { emitNoteChange } from '@/lib/note-change-sync'
import { getNoteById } from '@/app/actions/notes'
import { useNoteRefresh } from './NoteRefreshContext'
import { toast } from 'sonner'
import { queryKeys } from '@/lib/query-keys'
import { useLanguage } from '@/lib/i18n'

// ===== INPUT TYPES =====
export interface CreateNotebookInput {
  name: string
  icon?: string
  color?: string
  parentId?: string | null
}

export interface UpdateNotebookInput {
  name?: string
  icon?: string
  color?: string
  parentId?: string | null
}

export interface CreateLabelInput {
  name: string
  color?: string
  notebookId: string
}

export interface UpdateLabelInput {
  name?: string
  color?: string
}

// ===== CONTEXT VALUE =====
export interface NotebooksContextValue {
  // État global
  notebooks: Notebook[]
  currentNotebook: Notebook | null // null = "Notes générales"
  currentLabels: Label[] // Labels du notebook actuel
  isLoading: boolean
  isMovingNote: boolean
  error: string | null

  // Labels from /api/labels (merged from LabelContext)
  labels: Label[]
  loading: boolean
  notebookId: string | null
  setNotebookId: (notebookId: string | null) => void
  addLabel: (name: string, color?: LabelColorName, labelNotebookId?: string | null) => Promise<void>
  refreshLabels: () => Promise<void>
  getLabelColor: (name: string) => LabelColorName

  // Actions: Notebooks
  createNotebookOptimistic: (data: CreateNotebookInput) => Promise<void>
  updateNotebook: (notebookId: string, data: UpdateNotebookInput) => Promise<void>
  moveNotebookToParent: (notebookId: string, parentId: string | null) => Promise<void>
  deleteNotebook: (notebookId: string) => Promise<void>
  trashNotebook: (notebookId: string) => Promise<void>
  restoreNotebook: (notebookId: string) => Promise<void>
  permanentDeleteNotebook: (notebookId: string) => Promise<void>
  updateNotebookOrderOptimistic: (notebookIds: string[]) => Promise<void>
  setCurrentNotebook: (notebook: Notebook | null) => void

  // Actions: Labels
  createLabel: (data: CreateLabelInput) => Promise<Label>
  updateLabel: (labelId: string, data: UpdateLabelInput) => Promise<void>
  deleteLabel: (labelId: string) => Promise<void>

  // Actions: Notes
  moveNoteToNotebookOptimistic: (noteId: string, notebookId: string | null) => Promise<void>

  /** Recharger la liste des carnets (ex. après maintenance étiquettes) */
  refreshNotebooks: () => Promise<void>

  // Actions: AI (stubs pour l'instant)
  suggestNotebookForNote: (noteContent: string) => Promise<Notebook | null>
  suggestLabelsForNote: (noteContent: string, notebookId: string) => Promise<string[]>
}

export const NotebooksContext = createContext<NotebooksContextValue | null>(null)

export function useNotebooks() {
  const context = useContext(NotebooksContext)
  if (!context) {
    throw new Error('useNotebooks must be used within NotebooksProvider')
  }
  return context
}

interface NotebooksProviderProps {
  children: React.ReactNode
  initialNotebooks?: Notebook[]
}

export function NotebooksProvider({ children, initialNotebooks = [] }: NotebooksProviderProps) {
  // ===== BASE STATE =====
  const queryClient = useQueryClient()
  const { t } = useLanguage()
  const [notebooks, setNotebooks] = useState<Notebook[]>(initialNotebooks)
  const [currentNotebook, setCurrentNotebook] = useState<Notebook | null>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [isMovingNote, setIsMovingNote] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const { triggerNotebooksRefresh, notebooksRefreshKey } = useNoteRefresh()

  // ===== LABEL STATE (merged from LabelContext) =====
  const [labels, setLabels] = useState<Label[]>([])
  const [loading, setLoading] = useState(true)
  const [notebookId, setNotebookId] = useState<string | null>(null)

  // ===== DERIVED STATE =====
  const currentLabels = useMemo(() => {
    if (!currentNotebook) return []
    return notebooks.find(nb => nb.id === currentNotebook.id)?.labels ?? []
  }, [currentNotebook, notebooks])

  // ===== DATA LOADING =====
  const loadNotebooks = useCallback(async () => {
    setIsLoading(true)
    setError(null)

    try {
      const response = await fetch('/api/notebooks')
      if (!response.ok) throw new Error('Failed to load notebooks')

      const data = await response.json()
      setNotebooks(data.notebooks || [])
    } catch (err) {
      console.error('Failed to load notebooks:', err)
      setError(err instanceof Error ? err.message : 'Unknown error')
    } finally {
      setIsLoading(false)
    }
  }, [])

  useEffect(() => {
    loadNotebooks()
  }, [loadNotebooks])

  useEffect(() => {
    if (notebooksRefreshKey > 0) loadNotebooks()
  }, [notebooksRefreshKey, loadNotebooks])

  // ===== LABEL FETCHING (merged from LabelContext) =====
  const fetchLabels = useCallback(async (nbId: string | null) => {
    try {
      setLoading(true)
      const url = new URL('/api/labels', window.location.origin)
      if (nbId) {
        url.searchParams.set('notebookId', nbId)
      }

      const response = await fetch(url.toString(), {
        cache: 'no-store',
        credentials: 'include'
      })
      const data = await response.json()
      if (data.success && data.data) {
        setLabels(data.data)
      }
    } catch (error) {
      console.error('Failed to fetch labels:', error)
    } finally {
      setLoading(false)
    }
  }, [])

  useEffect(() => {
    fetchLabels(notebookId)
  }, [notebookId, fetchLabels])

  // ===== ACTIONS: NOTEBOOKS =====
  const createNotebookOptimistic = useCallback(async (data: CreateNotebookInput) => {
    const response = await fetch('/api/notebooks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })

    if (!response.ok) {
      throw new Error('Failed to create notebook')
    }

    // Invalidate notebooks cache — React Query will re-fetch in bg
    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const updateNotebook = useCallback(async (notebookId: string, data: UpdateNotebookInput) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })

    if (!response.ok) {
      throw new Error('Failed to update notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const moveNotebookToParent = useCallback(async (notebookId: string, parentId: string | null) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ parentId }),
    })

    if (!response.ok) {
      const data = await response.json().catch(() => ({}))
      throw new Error(data.error || 'Failed to move notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const deleteNotebook = useCallback(async (notebookId: string) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'DELETE',
    })

    if (!response.ok) {
      throw new Error('Failed to delete notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const trashNotebook = useCallback(async (notebookId: string) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ trashedAt: new Date().toISOString() }),
    })

    if (!response.ok) {
      throw new Error('Failed to trash notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const restoreNotebook = useCallback(async (notebookId: string) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ trashedAt: null }),
    })

    if (!response.ok) {
      throw new Error('Failed to restore notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const permanentDeleteNotebook = useCallback(async (notebookId: string) => {
    const response = await fetch(`/api/notebooks/${notebookId}`, {
      method: 'DELETE',
    })

    if (!response.ok) {
      throw new Error('Failed to permanently delete notebook')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  const updateNotebookOrderOptimistic = useCallback(async (notebookIds: string[]) => {
    const response = await fetch('/api/notebooks/reorder', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ notebookIds }),
    })

    if (!response.ok) {
      throw new Error('Failed to update notebook order')
    }

    queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
    triggerNotebooksRefresh()
  }, [queryClient, triggerNotebooksRefresh])

  // ===== LABEL ACTIONS (merged from LabelContext) =====
  const addLabel = useCallback(async (name: string, color?: LabelColorName, labelNotebookId?: string | null) => {
    try {
      const labelColor = color || getHashColor(name);
      const finalNotebookId = labelNotebookId || notebookId

      const response = await fetch('/api/labels', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, color: labelColor, notebookId: finalNotebookId }),
      })
      const data = await response.json()
      if (data.success && data.data) {
        setLabels(prev => [...prev, data.data])
        // Invalidate labels cache
        queryClient.invalidateQueries({ queryKey: queryKeys.labels(finalNotebookId) })
      }
    } catch (error) {
      console.error('Failed to add label:', error)
      throw error
    }
  }, [notebookId, queryClient])

  const getLabelColor = useCallback((name: string): LabelColorName => {
    const label = labels.find(l => l.name.toLowerCase() === name.toLowerCase())
    return label?.color || 'gray'
  }, [labels])

  const refreshLabels = useCallback(async () => {
    await fetchLabels(notebookId)
    queryClient.invalidateQueries({ queryKey: queryKeys.labels(notebookId) })
  }, [fetchLabels, notebookId, queryClient])

  // ===== ACTIONS: LABELS (keep existing API-based ones for compatibility) =====
  const createLabel = useCallback(async (data: CreateLabelInput) => {
    const response = await fetch('/api/labels', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })

    if (!response.ok) {
      throw new Error('Failed to create label')
    }

    const result = await response.json()
    // Invalidate labels cache for this notebook
    queryClient.invalidateQueries({ queryKey: queryKeys.labels(data.notebookId) })
    return result
  }, [queryClient])

  const updateLabel = useCallback(async (labelId: string, data: UpdateLabelInput) => {
    const response = await fetch(`/api/labels/${labelId}`, {
      method: 'PATCH',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    })

    if (!response.ok) {
      throw new Error('Failed to update label')
    }

    const result = await response.json()
    if (result.success && result.data) {
      setLabels(prev => prev.map(label =>
        label.id === labelId ? { ...label, ...result.data } : label
      ))
    }
    // Invalidate labels cache
    queryClient.invalidateQueries({ queryKey: queryKeys.labels(notebookId) })
  }, [notebookId, queryClient])

  const deleteLabel = useCallback(async (labelId: string) => {
    const response = await fetch(`/api/labels/${labelId}`, {
      method: 'DELETE',
    })

    if (!response.ok) {
      throw new Error('Failed to delete label')
    }

    setLabels(prev => prev.filter(label => label.id !== labelId))
    // Invalidate labels cache
    queryClient.invalidateQueries({ queryKey: queryKeys.labels(notebookId) })
  }, [notebookId, queryClient])

  // ===== ACTIONS: NOTES =====
  const moveNoteToNotebookOptimistic = useCallback(async (noteId: string, targetNotebookId: string | null) => {
    setIsMovingNote(true)
    try {
      const response = await fetch(`/api/notes/${noteId}/move`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ notebookId: targetNotebookId }),
      })

      if (!response.ok) {
        throw new Error('Failed to move note')
      }

      queryClient.invalidateQueries({ queryKey: queryKeys.notebooks() })
      queryClient.invalidateQueries({ queryKey: queryKeys.notes(null) })
      triggerNotebooksRefresh()
      const movedNote = await getNoteById(noteId)
      if (movedNote) {
        emitNoteChange({ type: 'updated', note: movedNote })
      }
    } catch (error) {
      toast.error(t('notes.moveFailed'))
      throw error
    } finally {
      setIsMovingNote(false)
    }
  }, [queryClient, triggerNotebooksRefresh, t])
  const suggestNotebookForNote = useCallback(async (_noteContent: string) => {
    // Stub pour l'instant - retourne null
    return null
  }, [])

  const suggestLabelsForNote = useCallback(async (_noteContent: string, _notebookId: string) => {
    // Stub pour l'instant - retourne tableau vide
    return []
  }, [])

  // ===== CONTEXT VALUE =====
  const value: NotebooksContextValue = useMemo(() => ({
    notebooks,
    currentNotebook,
    currentLabels,
    isLoading,
    isMovingNote,
    error,
    labels,
    loading,
    notebookId,
    setNotebookId,
    addLabel,
    refreshLabels,
    getLabelColor,
    createNotebookOptimistic,
    updateNotebook,
    moveNotebookToParent,
    deleteNotebook,
    trashNotebook,
    restoreNotebook,
    permanentDeleteNotebook,
    updateNotebookOrderOptimistic,
    setCurrentNotebook,
    createLabel,
    updateLabel,
    deleteLabel,
    moveNoteToNotebookOptimistic,
    refreshNotebooks: loadNotebooks,
    suggestNotebookForNote,
    suggestLabelsForNote,
  }), [
    notebooks,
    currentNotebook,
    currentLabels,
    isLoading,
    isMovingNote,
    error,
    labels,
    loading,
    notebookId,
    addLabel,
    refreshLabels,
    getLabelColor,
    createNotebookOptimistic,
    updateNotebook,
    moveNotebookToParent,
    deleteNotebook,
    trashNotebook,
    restoreNotebook,
    permanentDeleteNotebook,
    updateNotebookOrderOptimistic,
    createLabel,
    updateLabel,
    deleteLabel,
    moveNoteToNotebookOptimistic,
    loadNotebooks,
    suggestNotebookForNote,
    suggestLabelsForNote,
  ])

  return (
    <NotebooksContext.Provider value={value}>
      {children}
    </NotebooksContext.Provider>
  )
}