'use client'

import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from 'react'
import { fr } from 'date-fns/locale/fr'
import { enUS } from 'date-fns/locale/en-US'
import { faIR } from 'date-fns/locale/fa-IR'
import { formatAbsoluteDateLocalized } from '@/lib/utils/format-localized-date'
import * as Diff from 'diff'
import {
  History, Loader2, RotateCcw, Trash2, GitBranchPlus, Check, GitCompare, X,
} from 'lucide-react'
import { toast } from 'sonner'
import {
  getNoteHistory, restoreNoteVersion, deleteNoteHistoryEntry,
} from '@/app/actions/notes'
import { Button } from '@/components/ui/button'
import {
  AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import {
  Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle,
} from '@/components/ui/dialog'
import { MarkdownContent } from '@/components/markdown-content'
import { useLanguage } from '@/lib/i18n'
import type { Note, NoteHistoryEntry } from '@/lib/types'
import { cn } from '@/lib/utils'

interface NoteHistoryModalProps {
  open: boolean
  onOpenChange: (open: boolean) => void
  note: Note | null
  enabled: boolean
  onEnableHistory: () => Promise<void>
  onRestored: (note: Note) => void
}

type ViewMode = 'preview' | 'diff'

function getDateLocale(language: string) {
  if (language === 'fr') return fr
  if (language === 'fa') return faIR
  return enUS
}

function fmtDate(date: Date | string, language: string): string {
  const d = typeof date === 'string' ? new Date(date) : date
  return formatAbsoluteDateLocalized(d, language, 'd MMM yyyy HH:mm', getDateLocale(language))
}

function VersionPreview({ entry, language }: { entry: NoteHistoryEntry; language: string }) {
  const { t } = useLanguage()
  const isMd = entry.type === 'markdown' || entry.isMarkdown
  const isCl = entry.type === 'checklist'
  const isRt = entry.type === 'richtext'

  return (
    <div className="space-y-4">
      <div>
        <p className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1">
          {t('noteHistory.title')}
        </p>
        <p className="text-sm text-foreground font-medium">
          {entry.title || t('noteHistory.untitled')}
        </p>
      </div>
      <div>
        <p className="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground mb-1">
          {t('noteHistory.content')}
        </p>
        <div className="rounded-md border border-border/70 bg-muted/30 p-3 text-sm text-foreground overflow-auto max-h-[48vh]">
          {isCl && entry.checkItems ? (
            <div className="space-y-1">
              {entry.checkItems.map((item, i) => (
                <div key={i} className="flex items-center gap-2">
                  <span className={cn(
                    'flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded border text-[10px]',
                    item.checked ? 'border-primary bg-primary/20 text-primary' : 'border-border'
                  )}>{item.checked && '✓'}</span>
                  <span className={cn(item.checked && 'line-through text-muted-foreground')}>{item.text}</span>
                </div>
              ))}
            </div>
          ) : isMd ? (
            <div className="prose prose-sm dark:prose-invert max-w-none">
              <MarkdownContent content={entry.content || ''} />
            </div>
          ) : isRt ? (
            <div dangerouslySetInnerHTML={{ __html: require('isomorphic-dompurify').sanitize(entry.content || '') }} />
          ) : (
            <pre className="whitespace-pre-wrap font-sans">{entry.content || ''}</pre>
          )}
        </div>
      </div>
    </div>
  )
}

interface DiffLine { type: 'added' | 'removed' | 'unchanged'; value: string }

function computeLineDiff(oldText: string, newText: string) {
  const changes = Diff.diffLines(oldText || '', newText || '')
  const left: DiffLine[] = []
  const right: DiffLine[] = []
  for (const change of changes) {
    const lines = change.value.replace(/\n$/, '').split('\n')
    if (change.added) {
      for (const line of lines) { right.push({ type: 'added', value: line }); left.push({ type: 'unchanged', value: '' }) }
    } else if (change.removed) {
      for (const line of lines) { left.push({ type: 'removed', value: line }); right.push({ type: 'unchanged', value: '' }) }
    } else {
      for (const line of lines) { left.push({ type: 'unchanged', value: line }); right.push({ type: 'unchanged', value: line }) }
    }
  }
  return { left, right }
}

function DiffPanel({ lines, label, scrollRef, onScroll }: {
  lines: DiffLine[]; label: string
  scrollRef: React.RefObject<HTMLDivElement | null>
  onScroll: () => void
}) {
  return (
    <div className="flex-1 min-w-0 flex flex-col">
      <p className="text-[11px] font-semibold text-muted-foreground mb-1.5 shrink-0">{label}</p>
      <div
        ref={scrollRef}
        onScroll={onScroll}
        className="rounded-md border border-border/70 bg-muted/30 overflow-auto max-h-[48vh] text-xs font-mono leading-relaxed flex-1"
      >
        {lines.map((line, i) => (
          <div
            key={i}
            className={cn(
              'px-2.5 py-px min-h-[1.4em]',
              line.type === 'added' && 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
              line.type === 'removed' && 'bg-red-500/15 text-red-700 dark:text-red-300'
            )}
          >
            {line.value || '\u00A0'}
          </div>
        ))}
      </div>
    </div>
  )
}

export function NoteHistoryModal({
  open, onOpenChange, note, enabled, onEnableHistory, onRestored,
}: NoteHistoryModalProps) {
  const { t, language } = useLanguage()
  const [entries, setEntries] = useState<NoteHistoryEntry[]>([])
  const [selectedId, setSelectedId] = useState<string | null>(null)
  const [isLoading, setIsLoading] = useState(false)
  const [isRestoring, setIsRestoring] = useState(false)
  const [isEnabling, startEnableTx] = useTransition()
  const [viewMode, setViewMode] = useState<ViewMode>('preview')
  const [diffLeftId, setDiffLeftId] = useState<string | null>(null)
  const [diffRightId, setDiffRightId] = useState<string | null>(null)
  const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null)
  const [currentEntryId, setCurrentEntryId] = useState<string | null>(null)
  const leftScrollRef = useRef<HTMLDivElement>(null)
  const rightScrollRef = useRef<HTMLDivElement>(null)
  const isScrollSyncing = useRef(false)

  // Reset state when modal closes or note changes
  const prevNoteId = useRef<string | null>(null)
  useEffect(() => {
    if (!open) {
      setEntries([])
      setSelectedId(null)
      setViewMode('preview')
      setDiffLeftId(null)
      setDiffRightId(null)
      setCurrentEntryId(null)
      setIsLoading(false)
      return
    }
    if (!note) return
    if (note.id === prevNoteId.current && entries.length > 0) return
    prevNoteId.current = note.id

    let cancelled = false
    setIsLoading(true)
    getNoteHistory(note.id, 50)
      .then((result) => {
        if (cancelled) return
        setEntries(result)
        setSelectedId(result[0]?.id ?? null)
        setCurrentEntryId(result[0]?.id ?? null)
      })
      .catch(() => toast.error(t('general.error')))
      .finally(() => { if (!cancelled) setIsLoading(false) })

    return () => { cancelled = true }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, note?.id])

  // Separate effect: fetch when enabled flips to true for this note
  useEffect(() => {
    if (!open || !note || !enabled) return
    if (entries.length > 0) return

    let cancelled = false
    setIsLoading(true)
    getNoteHistory(note.id, 50)
      .then((result) => {
        if (cancelled) return
        setEntries(result)
        setSelectedId(result[0]?.id ?? null)
        setCurrentEntryId(result[0]?.id ?? null)
      })
      .catch(() => toast.error(t('general.error')))
      .finally(() => { if (!cancelled) setIsLoading(false) })

    return () => { cancelled = true }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [enabled])

  const currentVersion = useMemo(
    () => entries.find((e) => e.id === currentEntryId) ?? null,
    [entries, currentEntryId]
  )

  const selectedEntry = useMemo(
    () => entries.find((e) => e.id === selectedId) ?? null,
    [entries, selectedId]
  )
  const diffLeftEntry = useMemo(() => entries.find((e) => e.id === diffLeftId) ?? null, [entries, diffLeftId])
  const diffRightEntry = useMemo(() => entries.find((e) => e.id === diffRightId) ?? null, [entries, diffRightId])

  const diffResult = useMemo(() => {
    if (!diffLeftEntry || !diffRightEntry) return null
    return computeLineDiff(diffLeftEntry.content, diffRightEntry.content)
  }, [diffLeftEntry, diffRightEntry])

  const isDiffReady = !!(diffLeftId && diffRightId && diffLeftId !== diffRightId)

  const handleRestore = useCallback(async (entryId: string) => {
    if (!note) return
    const entry = entries.find((e) => e.id === entryId)
    if (!entry) return
    setIsRestoring(true)
    try {
      const restored = await restoreNoteVersion(note.id, entryId)
      setCurrentEntryId(entryId)
      toast.success(t('notes.historyRestored') || 'Version restaurée')
      onOpenChange(false)
      onRestored(restored)
    } catch {
      toast.error(t('general.error'))
    } finally {
      setIsRestoring(false)
    }
  }, [note, entries, onOpenChange, t, onRestored])

  const handleDelete = useCallback((entryId: string) => {
    setDeleteTargetId(entryId)
  }, [])

  const confirmDelete = useCallback(async () => {
    if (!note || !deleteTargetId) return
    const entryId = deleteTargetId
    setDeleteTargetId(null)
    setIsRestoring(true)
    try {
      await deleteNoteHistoryEntry(note.id, entryId)
      setEntries((prev) => prev.filter((e) => e.id !== entryId))
      if (selectedId === entryId) setSelectedId(entries[0]?.id ?? null)
      if (diffLeftId === entryId) setDiffLeftId(null)
      if (diffRightId === entryId) setDiffRightId(null)
      toast.success(t('notes.versionDeleted') || 'Version supprimée')
    } catch {
      toast.error(t('general.error'))
    } finally {
      setIsRestoring(false)
    }
  }, [note, entries, selectedId, diffLeftId, diffRightId, deleteTargetId, t])

  const handleEnable = () => {
    startEnableTx(async () => {
      try {
        await onEnableHistory()
        toast.success(t('notes.historyEnabled') || 'Historique activé')
      } catch {
        toast.error(t('general.error'))
      }
    })
  }

  const handleSyncScroll = (source: 'left' | 'right') => {
    if (isScrollSyncing.current) return
    isScrollSyncing.current = true
    const src = source === 'left' ? leftScrollRef.current : rightScrollRef.current
    const tgt = source === 'left' ? rightScrollRef.current : leftScrollRef.current
    if (src && tgt) {
      const ratio = src.scrollTop / (src.scrollHeight - src.clientHeight || 1)
      tgt.scrollTop = ratio * (tgt.scrollHeight - tgt.clientHeight)
    }
    requestAnimationFrame(() => { isScrollSyncing.current = false })
  }

  const noteTitle = note?.title || t('notes.untitled') || 'Sans titre'

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className={cn(
          'p-0 gap-0 sm:!max-w-none',
          viewMode === 'diff' && isDiffReady ? 'w-[92vw] max-w-[1500px]' : 'w-auto min-w-[500px] max-w-[800px]'
        )}
      >
        {/* ── Header ── */}
        <div className="border-b border-border/60 px-5 py-3 pe-10">
          <DialogTitle className="flex items-center gap-2 text-sm">
            <History className="h-4 w-4 text-primary" />
            {t('notes.history') || 'Historique'}
          </DialogTitle>
          <DialogDescription className="text-xs text-muted-foreground mt-0.5 truncate">
            {noteTitle}
          </DialogDescription>
        </div>

        {/* ── Body ── */}
        {!enabled ? (
          <div className="flex flex-col items-center justify-center px-6 py-16 text-center">
            <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-primary/10 mb-5">
              <GitBranchPlus className="h-8 w-8 text-primary" />
            </div>
            <h3 className="text-base font-semibold text-foreground mb-1.5">
              {t('notes.historyDisabledTitle') || 'Historique des versions'}
            </h3>
            <p className="text-sm text-muted-foreground max-w-xs mb-6 leading-relaxed">
              {t('notes.historyDisabledDesc') || "Activez l'historique pour enregistrer les versions."}
            </p>
            <Button onClick={handleEnable} disabled={isEnabling} size="lg" className="rounded-full px-8">
              {isEnabling && <Loader2 className="me-2 h-4 w-4 animate-spin" />}
              {t('notes.enableHistory') || "Activer l'historique"}
            </Button>
          </div>
        ) : isLoading ? (
          <div className="flex items-center justify-center py-16 text-sm text-muted-foreground gap-2">
            <Loader2 className="h-4 w-4 animate-spin" />
            {t('general.loading')}
          </div>
        ) : entries.length === 0 ? (
          <div className="flex flex-col items-center justify-center py-16 text-center">
            <History className="h-8 w-8 text-muted-foreground/30 mb-3" />
            <p className="text-sm text-muted-foreground">{t('notes.historyEmpty') || 'Aucune version disponible'}</p>
          </div>
        ) : (
          <div className="grid grid-cols-[210px_1fr]">
            {/* ── Left: Version list ── */}
            <div className="max-h-[65vh] overflow-y-auto border-e border-border/60 p-2 space-y-1">
              {entries.map((entry) => {
                const isCurrent = entry.version === currentVersion?.version
                const isSelected = viewMode === 'preview' && selectedId === entry.id
                const isDL = entry.id === diffLeftId
                const isDR = entry.id === diffRightId
                const isDiffSel = viewMode === 'diff' && (isDL || isDR)

                return (
                  <div
                    key={entry.id}
                    onClick={() => {
                      if (viewMode === 'diff') {
                        if (!diffLeftId) { setDiffLeftId(entry.id) }
                        else if (!diffRightId && entry.id !== diffLeftId) { setDiffRightId(entry.id) }
                        else { setDiffLeftId(entry.id); setDiffRightId(null) }
                      } else {
                        setSelectedId(entry.id)
                      }
                    }}
                    className={cn(
                      'group/entry relative rounded-md border px-2.5 py-1.5 cursor-pointer transition-colors',
                      isDiffSel
                        ? isDL ? 'border-red-400/50 bg-red-500/10' : 'border-emerald-400/50 bg-emerald-500/10'
                        : isSelected ? 'border-primary/40 bg-primary/8' : 'border-transparent hover:bg-muted/60'
                    )}
                  >
                    <div className="flex items-center gap-1.5 pe-10">
                      <span className="text-xs font-semibold text-foreground">v{entry.version}</span>
                      {isCurrent && (
                        <span className="rounded bg-primary/15 px-1.5 py-px text-[10px] font-medium text-primary whitespace-nowrap">
                          {t('notes.currentVersion') || 'actuelle'}
                        </span>
                      )}
                    </div>
                    <p className="text-[11px] text-muted-foreground" suppressHydrationWarning>
                      {fmtDate(entry.createdAt, language)}
                    </p>

                    <div className={cn(
                      'absolute end-1 top-1/2 -translate-y-1/2 flex items-center gap-0.5',
                      isSelected || isDiffSel ? 'opacity-100' : 'opacity-0 group-hover/entry:opacity-100'
                    )}>
                      {viewMode === 'preview' && (
                        <button
                          type="button" onClick={(e) => { e.stopPropagation(); handleRestore(entry.id) }}
                          disabled={isRestoring}
                          className="rounded p-1 text-muted-foreground/60 hover:text-primary hover:bg-primary/10"
                          title={t('notes.restore') || 'Restaurer'}
                        >
                          {isRestoring ? <Loader2 className="h-3 w-3 animate-spin" /> : <RotateCcw className="h-3 w-3" />}
                        </button>
                      )}
                      <button
                        type="button" onClick={(e) => { e.stopPropagation(); handleDelete(entry.id) }}
                        className="rounded p-1 text-muted-foreground/60 hover:text-red-500 hover:bg-red-500/10"
                        title={t('notes.deleteVersion') || 'Supprimer'}
                      >
                        <Trash2 className="h-3 w-3" />
                      </button>
                    </div>
                  </div>
                )
              })}

              {viewMode === 'diff' && entries.length >= 2 && (
                <div className="mt-2 border-t border-border/40 pt-2 px-1">
                  <p className="text-[10px] text-muted-foreground mb-1.5">
                    {t('notes.diffSelectHint') || 'Cliquez sur 2 versions pour comparer'}
                  </p>
                  <div className="flex items-center gap-2 text-[11px]">
                    <span className="flex items-center gap-1">
                      <span className="h-2 w-2 rounded-full bg-red-400 inline-block" />
                      {diffLeftEntry ? `v${diffLeftEntry.version}` : '—'}
                    </span>
                    <span className="text-muted-foreground">→</span>
                    <span className="flex items-center gap-1">
                      <span className="h-2 w-2 rounded-full bg-emerald-400 inline-block" />
                      {diffRightEntry ? `v${diffRightEntry.version}` : '—'}
                    </span>
                    {isDiffReady && (
                      <button
                        type="button"
                        onClick={() => { setDiffLeftId(null); setDiffRightId(null) }}
                        className="ml-auto p-0.5 text-muted-foreground hover:text-foreground"
                      >
                        <X className="h-3 w-3" />
                      </button>
                    )}
                  </div>
                </div>
              )}
            </div>

            {/* ── Right: Preview / Diff ── */}
            <div className="max-h-[65vh] overflow-y-auto px-5 py-4">
              {viewMode === 'preview' ? (
                selectedEntry ? (
                  <VersionPreview entry={selectedEntry} language={language} />
                ) : (
                  <p className="text-sm text-muted-foreground py-8 text-center">
                    {t('notes.historySelectVersion') || 'Sélectionnez une version pour la prévisualiser'}
                  </p>
                )
              ) : isDiffReady && diffResult ? (
                <div className="space-y-3">
                  <p className="text-xs font-semibold text-muted-foreground">
                    {t('notes.diffTitle') || 'Comparaison'} : v{diffLeftEntry!.version} → v{diffRightEntry!.version}
                  </p>
                  <div className="flex gap-3">
                    <DiffPanel
                      lines={diffResult.left}
                      label={`v${diffLeftEntry!.version} — ${fmtDate(diffLeftEntry!.createdAt, language)}`}
                      scrollRef={leftScrollRef}
                      onScroll={() => handleSyncScroll('left')}
                    />
                    <DiffPanel
                      lines={diffResult.right}
                      label={`v${diffRightEntry!.version} — ${fmtDate(diffRightEntry!.createdAt, language)}`}
                      scrollRef={rightScrollRef}
                      onScroll={() => handleSyncScroll('right')}
                    />
                  </div>
                </div>
              ) : (
                <div className="flex flex-col items-center justify-center py-12 text-center">
                  <GitCompare className="h-8 w-8 text-muted-foreground/30 mb-3" />
                  <p className="text-sm text-muted-foreground">
                    {t('notes.diffSelectHint') || 'Cliquez sur 2 versions pour comparer'}
                  </p>
                </div>
              )}
            </div>
          </div>
        )}

        {/* ── Footer: actions ── */}
        {enabled && !isLoading && entries.length >= 2 && (
          <div className="flex items-center justify-end border-t border-border/60 px-5 py-2.5">
            <div className="flex items-center gap-1 rounded-lg border border-border/60 p-0.5">
              <button
                type="button"
                onClick={() => { setViewMode('preview'); setDiffLeftId(null); setDiffRightId(null) }}
                className={cn(
                  'rounded-md px-2.5 py-1 text-xs font-medium transition-colors',
                  viewMode === 'preview' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
                )}
              >
                {t('notes.history') || 'Historique'}
              </button>
              <button
                type="button"
                onClick={() => {
                  setViewMode('diff')
                  if (!diffLeftId && entries.length >= 2) {
                    setDiffLeftId(entries[1].id)
                    setDiffRightId(entries[0].id)
                  }
                }}
                className={cn(
                  'rounded-md px-2.5 py-1 text-xs font-medium transition-colors inline-flex items-center gap-1',
                  viewMode === 'diff' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'
                )}
              >
                <GitCompare className="h-3 w-3" />
                {t('notes.compareVersions') || 'Comparer'}
              </button>
            </div>
          </div>
        )}
      </DialogContent>

      <AlertDialog open={!!deleteTargetId} onOpenChange={(open) => { if (!open) setDeleteTargetId(null) }}>
        <AlertDialogContent size="sm">
          <AlertDialogHeader>
            <AlertDialogTitle>{t('notes.deleteVersionConfirm') || 'Supprimer cette version définitivement ?'}</AlertDialogTitle>
            <AlertDialogDescription>
              {t('notes.deleteVersionDesc') || 'Cette action est irréversible. La version sera définitivement supprimée de l\'historique.'}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>{t('general.cancel') || 'Annuler'}</AlertDialogCancel>
            <AlertDialogAction variant="destructive" onClick={confirmDelete}>
              {t('general.delete') || 'Supprimer'}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </Dialog>
  )
}
