import { useState, useEffect, useCallback, useMemo } from 'react'
import Markdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import remarkBreaks from 'remark-breaks'
import ReactDOM from 'react-dom'
import { Plus, Pencil, X, StickyNote, Settings } from 'lucide-react'
import { collabApi } from '../../api/client'
import { useCanDo } from '../../store/permissionsStore'
import { useTripStore } from '../../store/tripStore'
import { addListener, removeListener } from '../../api/websocket'
import { useTranslation } from '../../i18n'
import { useToast } from '../shared/Toast'
import type { User } from '../../types'
import type { CollabNote } from './CollabNotes.types'
import { FONT, NOTE_COLORS } from './CollabNotes.constants'
import { NoteFormModal } from './CollabNotesFormModal'
import { CategorySettingsModal } from './CollabNotesCategorySettingsModal'
import { NoteCard } from './CollabNotesCard'
import { FilePreviewPortal } from './CollabNotesFilePreviewPortal'
import { AuthedImg } from './CollabNotesAuthedImg'

// ── Main Component ──────────────────────────────────────────────────────────
interface CollabNotesProps {
  tripId: number
  currentUser: User
}

/**
 * Collab notes state: load + WebSocket sync, note CRUD (with file uploads),
 * category colors/renames and the view/edit/settings modal toggles. The shell
 * below renders the header, category pills, the note grid and the modals.
 */
function useCollabNotes({ tripId, currentUser }: CollabNotesProps) {
  const { t } = useTranslation()
  const toast = useToast()
  const can = useCanDo()
  const trip = useTripStore((s) => s.trip)
  const canEdit = can('collab_edit', trip)
  const [notes, setNotes] = useState([])
  const [loading, setLoading] = useState(true)
  const [showNewModal, setShowNewModal] = useState(false)
  const [editingNote, setEditingNote] = useState(null)
  const [viewingNote, setViewingNote] = useState<CollabNote | null>(null)
  const [previewFile, setPreviewFile] = useState(null)
  const [showSettings, setShowSettings] = useState(false)
  const [activeCategory, setActiveCategory] = useState(null)

  // Empty categories (no notes yet) stored in localStorage
  const [emptyCategories, setEmptyCategories] = useState(() => {
    try { return JSON.parse(localStorage.getItem(`collab-cats-${tripId}`)) || {} } catch { return {} }
  })
  const saveEmptyCategories = (map) => {
    setEmptyCategories(map)
    localStorage.setItem(`collab-cats-${tripId}`, JSON.stringify(map))
  }

  // Category colors: from notes first, then from empty categories
  const categoryColors = useMemo(() => {
    const map = { ...emptyCategories }
    for (const n of notes) {
      if (n.category && n.color) map[n.category] = n.color
    }
    return map
  }, [notes, emptyCategories])

  const getCategoryColor = (cat) => {
    if (!cat) return NOTE_COLORS[0].value
    if (categoryColors[cat]) return categoryColors[cat]
    return NOTE_COLORS[Object.keys(categoryColors).length % NOTE_COLORS.length].value
  }

  // ── Load notes on mount ──
  useEffect(() => {
    if (!tripId) return
    let cancelled = false
    setLoading(true)
    collabApi.getNotes(tripId)
      .then(data => { if (!cancelled) setNotes(data?.notes || data || []) })
      .catch(() => { if (!cancelled) setNotes([]) })
      .finally(() => { if (!cancelled) setLoading(false) })
    return () => { cancelled = true }
  }, [tripId])

  // ── WebSocket real-time sync ──
  useEffect(() => {
    if (!tripId) return

    const handler = (msg) => {
      if (msg.type === 'collab:note:created' && msg.note) {
        setNotes(prev => {
          if (prev.some(n => n.id === msg.note.id)) return prev
          return [msg.note, ...prev]
        })
      }
      if (msg.type === 'collab:note:updated' && msg.note) {
        setNotes(prev =>
          prev.map(n => (n.id === msg.note.id ? { ...n, ...msg.note } : n))
        )
      }
      if (msg.type === 'collab:note:deleted') {
        const deletedId = msg.noteId || msg.id
        if (deletedId) {
          setNotes(prev => prev.filter(n => n.id !== deletedId))
        }
      }
    }

    addListener(handler)
    return () => removeListener(handler)
  }, [tripId])

  // ── Actions ──
  const handleCreateNote = useCallback(async (data) => {
    const pendingFiles = data._pendingFiles || []
    delete data._pendingFiles
    let created
    try {
      created = await collabApi.createNote(tripId, data)
    } catch (err) {
      toast.error(t('common.error'))
      throw err
    }
    if (created) {
      const note = created.note || created
      // Upload pending files
      if (pendingFiles.length > 0 && note.id) {
        for (const file of pendingFiles) {
          const fd = new FormData()
          fd.append('file', file)
          try { await collabApi.uploadNoteFile(tripId, note.id, fd) } catch (err) { console.error('Failed to upload note attachment:', err); toast.error(t('common.error')) }
        }
        // Reload note with attachments
        const fresh = await collabApi.getNotes(tripId)
        if (fresh?.notes) setNotes(fresh.notes)
        window.dispatchEvent(new Event('collab-files-changed'))
        return
      }
      setNotes(prev => {
        if (prev.some(n => n.id === note.id)) return prev
        return [note, ...prev]
      })
    }
  }, [tripId, toast, t])

  const handleUpdateNote = useCallback(async (noteId, data) => {
    let result
    try {
      result = await collabApi.updateNote(tripId, noteId, data)
    } catch (err) {
      toast.error(t('common.error'))
      throw err
    }
    const updated = result?.note || result
    if (updated) {
      setNotes(prev =>
        prev.map(n => (n.id === noteId ? { ...n, ...updated } : n))
      )
    }
  }, [tripId, toast, t])

  const saveCategoryColors = useCallback(async (newMap) => {
    // Update notes with changed colors
    for (const [cat, color] of Object.entries(newMap)) {
      const notesInCat = notes.filter(n => n.category === cat)
      if (notesInCat.length > 0 && categoryColors[cat] !== color) {
        for (const n of notesInCat) {
          await handleUpdateNote(n.id, { color })
        }
      }
    }
    // Save all categories (including empty ones) to localStorage
    const emptyCats = {}
    for (const [cat, color] of Object.entries(newMap)) {
      if (!notes.some(n => n.category === cat)) {
        emptyCats[cat] = color
      }
    }
    saveEmptyCategories(emptyCats)
  }, [categoryColors, notes, handleUpdateNote])

  const handleEditSubmit = useCallback(async (data) => {
    if (!editingNote) return
    const pendingFiles = data._pendingFiles || []
    delete data._pendingFiles
    await handleUpdateNote(editingNote.id, data)
    if (pendingFiles.length > 0) {
      for (const file of pendingFiles) {
        const fd = new FormData()
        fd.append('file', file)
        try { await collabApi.uploadNoteFile(tripId, editingNote.id, fd) } catch { toast.error(t('common.error')) }
      }
      const fresh = await collabApi.getNotes(tripId)
      if (fresh?.notes) setNotes(fresh.notes)
      window.dispatchEvent(new Event('collab-files-changed'))
    }
  }, [editingNote, tripId, handleUpdateNote, toast, t])

  const handleDeleteNoteFile = useCallback(async (noteId, fileId) => {
    try { await collabApi.deleteNoteFile(tripId, noteId, fileId) } catch { toast.error(t('common.error')) }
    window.dispatchEvent(new Event('collab-files-changed'))
  }, [tripId, toast, t])

  const handleDeleteNote = useCallback(async (noteId) => {
    try {
      await collabApi.deleteNote(tripId, noteId)
    } catch (err) {
      toast.error(t('common.error'))
      throw err
    }
    setNotes(prev => prev.filter(n => n.id !== noteId))
    window.dispatchEvent(new Event('collab-files-changed'))
  }, [tripId, toast, t])

  // ── Derived data ──
  const categories = [...new Set(notes.map(n => n.category).filter(Boolean))]

  const sortedNotes = [...notes]
    .filter(n => activeCategory === null || n.category === activeCategory)
    .sort((a, b) => {
      if (a.pinned && !b.pinned) return -1
      if (!a.pinned && b.pinned) return 1
      const tA = new Date(a.updated_at || a.created_at || 0).getTime()
      const tB = new Date(b.updated_at || b.created_at || 0).getTime()
      return tB - tA
    })

  return {
    tripId, currentUser, t, canEdit,
    notes, loading, showNewModal, setShowNewModal, editingNote, setEditingNote,
    viewingNote, setViewingNote, previewFile, setPreviewFile, showSettings, setShowSettings,
    activeCategory, setActiveCategory, categoryColors, getCategoryColor,
    handleCreateNote, handleUpdateNote, saveCategoryColors, handleEditSubmit,
    handleDeleteNoteFile, handleDeleteNote, categories, sortedNotes,
  }
}

type NotesState = ReturnType<typeof useCollabNotes>

function CollabNotesLoading({ t }: NotesState) {
  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', fontFamily: FONT }}>
      <div style={{ padding: '12px 16px', borderBottom: '1px solid var(--border-faint)' }}>
        <h3 style={{ fontSize: 14, fontWeight: 700, color: 'var(--text-primary)', margin: 0, fontFamily: FONT }}>
          {t('collab.notes.title')}
        </h3>
      </div>
      <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <div style={{
          width: 20, height: 20, border: '2px solid var(--border-primary)',
          borderTopColor: 'var(--text-primary)', borderRadius: '50%',
          animation: 'collab-notes-spin 0.7s linear infinite',
        }} />
        <style>{`@keyframes collab-notes-spin { to { transform: rotate(360deg) } }`}</style>
      </div>
    </div>
  )
}

function CollabNotesHeader({ t, canEdit, setShowSettings, setShowNewModal }: NotesState) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '10px 16px', flexShrink: 0 }}>
      <h3 style={{
        fontSize: 12, fontWeight: 600, color: 'var(--text-muted)', margin: 0, fontFamily: FONT,
        letterSpacing: 0.3, textTransform: 'uppercase', display: 'flex', alignItems: 'center', gap: 7,
      }}>
        <StickyNote size={14} color="var(--text-faint)" />
        {t('collab.notes.title')}
      </h3>
      <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
        {canEdit && <button onClick={() => setShowSettings(true)} title={t('collab.notes.categorySettings') || 'Categories'}
          style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', width: 28, height: 28, borderRadius: 8, border: 'none', background: 'transparent', cursor: 'pointer', color: 'var(--text-faint)', transition: 'color 0.12s' }}
          onMouseEnter={e => e.currentTarget.style.color = 'var(--text-primary)'}
          onMouseLeave={e => e.currentTarget.style.color = 'var(--text-faint)'}>
          <Settings size={14} />
        </button>}
        {canEdit && <button onClick={() => setShowNewModal(true)}
          style={{ display: 'inline-flex', alignItems: 'center', gap: 4, borderRadius: 99, padding: '6px 12px', background: 'var(--accent)', color: 'var(--accent-text)', fontSize: 11, fontWeight: 600, fontFamily: FONT, border: 'none', cursor: 'pointer', whiteSpace: 'nowrap' }}>
          <Plus size={12} />
          {t('collab.notes.new')}
        </button>}
      </div>
    </div>
  )
}

function CollabCategoryPills({ categories, activeCategory, setActiveCategory, t }: NotesState) {
  return (
    <div style={{ display: 'flex', gap: 4, padding: '8px 12px 0', overflowX: 'auto', flexShrink: 0 }}>
      <button
        onClick={() => setActiveCategory(null)}
        style={{
          flexShrink: 0, borderRadius: 99, padding: '3px 10px', fontSize: 10, fontWeight: 600, fontFamily: FONT,
          border: activeCategory === null ? '1px solid var(--accent)' : '1px solid var(--border-faint)',
          background: activeCategory === null ? 'var(--accent)' : 'transparent',
          color: activeCategory === null ? 'var(--accent-text)' : 'var(--text-secondary)',
          cursor: 'pointer', whiteSpace: 'nowrap', textTransform: 'uppercase', letterSpacing: '0.03em',
        }}
      >
        {t('collab.notes.all')}
      </button>
      {categories.map(cat => (
        <button
          key={cat}
          onClick={() => setActiveCategory(prev => prev === cat ? null : cat)}
          style={{
            flexShrink: 0, borderRadius: 99, padding: '3px 10px', fontSize: 10, fontWeight: 600, fontFamily: FONT,
            border: activeCategory === cat ? '1px solid var(--accent)' : '1px solid var(--border-faint)',
            background: activeCategory === cat ? 'var(--accent)' : 'transparent',
            color: activeCategory === cat ? 'var(--accent-text)' : 'var(--text-secondary)',
            cursor: 'pointer', whiteSpace: 'nowrap', textTransform: 'uppercase', letterSpacing: '0.03em',
          }}
        >
          {cat}
        </button>
      ))}
    </div>
  )
}

function CollabNotesGrid(S: NotesState) {
  const {
    sortedNotes, currentUser, canEdit, handleUpdateNote, handleDeleteNote,
    setEditingNote, setViewingNote, setPreviewFile, getCategoryColor, tripId, t,
  } = S
  return (
    <div style={{ flex: 1, overflowY: 'auto', padding: 12 }}>
      {sortedNotes.length === 0 ? (
        /* ── Empty state ── */
        <div style={{
          display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
          padding: '48px 20px', textAlign: 'center', height: '100%',
        }}>
          <Pencil size={36} color="var(--text-faint)" style={{ marginBottom: 12 }} />
          <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4, fontFamily: FONT }}>
            {t('collab.notes.empty')}
          </div>
          <div style={{ fontSize: 12, color: 'var(--text-faint)', fontFamily: FONT }}>
            {t('collab.notes.emptyDesc') || 'Create a note to get started'}
          </div>
        </div>
      ) : (
        /* ── Notes grid — 2 columns ── */
        <div style={{
          display: 'grid',
          gridTemplateColumns: window.innerWidth < 768 ? '1fr' : 'repeat(2, 1fr)',
          gap: 8,
        }}>
          {sortedNotes.map(note => (
            <NoteCard
              key={note.id}
              note={note}
              currentUser={currentUser}
              canEdit={canEdit}
              onUpdate={handleUpdateNote}
              onDelete={handleDeleteNote}
              onEdit={setEditingNote}
              onView={setViewingNote}
              onPreviewFile={setPreviewFile}
              getCategoryColor={getCategoryColor}
              tripId={tripId}
              t={t}
            />
          ))}
        </div>
      )}
    </div>
  )
}

function ViewNoteModal(S: NotesState) {
  const { viewingNote, setViewingNote, canEdit, setEditingNote, getCategoryColor, t, setPreviewFile } = S
  if (!viewingNote) return null
  return ReactDOM.createPortal(
    <div
      style={{
        position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)',
        backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        zIndex: 10000, padding: 16,
      }}
      onClick={() => setViewingNote(null)}
    >
      <div
        style={{
          background: 'var(--bg-card)', borderRadius: 16,
          boxShadow: '0 20px 60px rgba(0,0,0,0.2)',
          width: 'min(700px, calc(100vw - 32px))', maxHeight: '80vh',
          overflow: 'hidden', display: 'flex', flexDirection: 'column',
        }}
        onClick={e => e.stopPropagation()}
      >
        <div style={{
          padding: '16px 20px 12px', borderBottom: '1px solid var(--border-primary)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
        }}>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 17, fontWeight: 600, color: 'var(--text-primary)' }}>{viewingNote.title}</div>
            {viewingNote.category && (
              <span style={{
                display: 'inline-block', marginTop: 4, fontSize: 10, fontWeight: 600,
                color: getCategoryColor(viewingNote.category),
                background: `${getCategoryColor(viewingNote.category)}18`,
                padding: '2px 8px', borderRadius: 6,
              }}>{viewingNote.category}</span>
            )}
          </div>
          <div style={{ display: 'flex', gap: 4, flexShrink: 0 }}>
            {canEdit && <button onClick={() => { setViewingNote(null); setEditingNote(viewingNote) }}
              style={{ padding: 6, background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-faint)', display: 'flex', borderRadius: 6 }}
              onMouseEnter={e => e.currentTarget.style.color = 'var(--text-primary)'}
              onMouseLeave={e => e.currentTarget.style.color = 'var(--text-faint)'}>
              <Pencil size={16} />
            </button>}
            <button onClick={() => setViewingNote(null)}
              style={{ padding: 6, background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-faint)', display: 'flex', borderRadius: 6 }}
              onMouseEnter={e => e.currentTarget.style.color = 'var(--text-primary)'}
              onMouseLeave={e => e.currentTarget.style.color = 'var(--text-faint)'}>
              <X size={18} />
            </button>
          </div>
        </div>
        <div className="collab-note-md-full" style={{ padding: '16px 20px', overflowY: 'auto', fontSize: 14, color: 'var(--text-primary)', lineHeight: 1.7 }}>
          <Markdown remarkPlugins={[remarkGfm, remarkBreaks]}>{viewingNote.content || ''}</Markdown>
          {(viewingNote.attachments || []).length > 0 && (
            <div style={{ marginTop: 16, paddingTop: 16, borderTop: '1px solid var(--border-primary)' }}>
              <div style={{ fontSize: 11, fontWeight: 600, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 10 }}>{t('files.title')}</div>
              <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                {(viewingNote.attachments || []).map(a => {
                  const isImage = a.mime_type?.startsWith('image/')
                  const ext = (a.original_name || '').split('.').pop()?.toUpperCase() || '?'
                  return (
                    <div key={a.id} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4, maxWidth: 72 }}>
                      {isImage ? (
                        <AuthedImg src={a.url} alt={a.original_name}
                          style={{ width: 64, height: 64, objectFit: 'cover', borderRadius: 8, cursor: 'pointer', transition: 'transform 0.12s, box-shadow 0.12s' }}
                          onClick={() => setPreviewFile(a)}
                          onMouseEnter={e => { e.currentTarget.style.transform = 'scale(1.06)'; e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)' }}
                          onMouseLeave={e => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.boxShadow = 'none' }} />
                      ) : (
                        <div title={a.original_name} onClick={() => setPreviewFile(a)}
                          style={{
                            width: 64, height: 64, borderRadius: 8, cursor: 'pointer',
                            background: a.mime_type === 'application/pdf' ? '#ef44441a' : 'var(--bg-secondary)',
                            display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 1,
                            transition: 'transform 0.12s, box-shadow 0.12s',
                          }}
                          onMouseEnter={e => { e.currentTarget.style.transform = 'scale(1.06)'; e.currentTarget.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)' }}
                          onMouseLeave={e => { e.currentTarget.style.transform = 'scale(1)'; e.currentTarget.style.boxShadow = 'none' }}>
                          <span style={{ fontSize: 10, fontWeight: 700, color: a.mime_type === 'application/pdf' ? '#ef4444' : 'var(--text-muted)', letterSpacing: 0.3 }}>{ext}</span>
                        </div>
                      )}
                      <span style={{ fontSize: 9, color: 'var(--text-faint)', textAlign: 'center', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', width: '100%' }}>{a.original_name}</span>
                    </div>
                  )
                })}
              </div>
            </div>
          )}
        </div>
      </div>
    </div>,
    document.body
  )
}

export default function CollabNotes(props: CollabNotesProps) {
  const S = useCollabNotes(props)
  const {
    loading, tripId, t, categories, categoryColors, getCategoryColor, notes,
    viewingNote, showNewModal, editingNote, previewFile, showSettings,
    setShowNewModal, setEditingNote, setPreviewFile, setShowSettings,
    handleCreateNote, handleEditSubmit, handleDeleteNoteFile, saveCategoryColors, handleUpdateNote,
  } = S

  if (loading) return <CollabNotesLoading {...S} />

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', fontFamily: FONT }}>
      <CollabNotesHeader {...S} />
      {categories.length > 0 && <CollabCategoryPills {...S} />}
      <CollabNotesGrid {...S} />

      {viewingNote && <ViewNoteModal {...S} />}

      {showNewModal && (
        <NoteFormModal
          note={null}
          tripId={tripId}
          onClose={() => setShowNewModal(false)}
          onSubmit={handleCreateNote}
          existingCategories={categories}
          categoryColors={categoryColors}
          getCategoryColor={getCategoryColor}
          t={t}
        />
      )}

      {editingNote && (
        <NoteFormModal
          note={editingNote}
          tripId={tripId}
          onClose={() => setEditingNote(null)}
          onSubmit={handleEditSubmit}
          onDeleteFile={handleDeleteNoteFile}
          existingCategories={categories}
          categoryColors={categoryColors}
          getCategoryColor={getCategoryColor}
          t={t}
        />
      )}

      <FilePreviewPortal file={previewFile} onClose={() => setPreviewFile(null)} />

      {showSettings && (
        <CategorySettingsModal
          onClose={() => setShowSettings(false)}
          categories={categories}
          categoryColors={categoryColors}
          onSave={saveCategoryColors}
          onRenameCategory={async (oldName, newName) => {
            // Update all notes with this category in DB
            const toUpdate = notes.filter(n => n.category === oldName)
            for (const n of toUpdate) {
              await handleUpdateNote(n.id, { category: newName })
            }
          }}
          t={t}
        />
      )}
    </div>
  )
}
