"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import type { ReactNode } from "react"
import { useEditor, EditorContent } from "@tiptap/react"
import { HocuspocusProvider } from "@hocuspocus/provider"
import * as Y from "yjs"
import { DocsChrome } from "@/components/drive/richtext/docs-chrome"
import { DocsPageView, DocsStatusBar } from "@/components/drive/richtext/docs-page-view"
import { DocsToolbar } from "@/components/drive/richtext/docs-toolbar"
import { buildRichTextExtensions, RICHTEXT_EDITOR_CLASS } from "@/lib/drive/richtext-extensions"
import type { RichTextSaveStatus, RichTextSessionResponse } from "@/lib/drive/richtext-types"
import type { DriveShare, DriveFileInfo } from "@/lib/api/types"
import { useDocsViewSettings } from "@/lib/drive/docs-view-settings"
import { useCollabPresence } from "@/lib/drive/use-collab-presence"
import { apiClient } from "@/lib/api/client"
import { driveDownloadApiPath } from "@/lib/api/drive-download"
import { importFileToTipTap } from "@/lib/drive/richtext-import"

const SAVE_DEBOUNCE_MS = 2000
/** Align with Hocuspocus store debounce + buffer */
const COLLAB_SAVE_IDLE_MS = 2000

export type RichTextDocsChromeProps = {
  title: string
  onRename?: (next: string) => Promise<void>
  renameDisabled?: boolean
  backHref?: string
  backLabel?: string
  showBack?: boolean
  shares?: DriveShare[]
  onShareClick?: () => void
  showShare?: boolean
  showAccount?: boolean
  trailing?: ReactNode
  moveFile?: DriveFileInfo
  onFileMoved?: (newPath: string) => void
}

export function RichTextDocumentEditor({
  session,
  mode,
  userName,
  userColor,
  onSaveStatus,
  fetchSourceBytes,
  importApi,
  chrome,
}: {
  session: RichTextSessionResponse
  mode: "edit" | "view"
  userName: string
  userColor: string
  onSaveStatus?: (status: RichTextSaveStatus) => void
  fetchSourceBytes?: (path: string) => Promise<ArrayBuffer>
  importApi?: (body: { source_path: string; content: Record<string, unknown> }) => Promise<void>
  chrome?: RichTextDocsChromeProps
}) {
  const editable = mode === "edit"
  const collaboration = session.collaboration && Boolean(session.wsUrl && session.token)
  const ydocRef = useRef<Y.Doc | null>(null)
  if (collaboration && !ydocRef.current) {
    ydocRef.current = new Y.Doc()
  }
  const ydoc = collaboration ? ydocRef.current : null

  const [provider, setProvider] = useState<HocuspocusProvider | null>(null)
  const [collabSynced, setCollabSynced] = useState(false)
  const [collabError, setCollabError] = useState<string | null>(null)
  const [importDone, setImportDone] = useState(!session.importRequired)
  const [saveStatus, setSaveStatus] = useState<RichTextSaveStatus>("idle")
  const [pageCount, setPageCount] = useState(1)
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
  const saveIdleTimer = useRef<ReturnType<typeof setTimeout> | null>(null)
  const saveStatusRef = useRef<RichTextSaveStatus>("idle")

  const { settings, setPageFormatId, setZoom, toggleSpellcheck, toggleChromeCollapsed } =
    useDocsViewSettings()
  const presenceUsers = useCollabPresence(provider, { name: userName, color: userColor })

  const reportSaveStatus = useCallback(
    (status: RichTextSaveStatus) => {
      saveStatusRef.current = status
      setSaveStatus(status)
      onSaveStatus?.(status)
    },
    [onSaveStatus]
  )

  const handlePageCountChange = useCallback((count: number) => {
    setPageCount(count)
  }, [])

  const markCollabDirty = useCallback(() => {
    if (saveStatusRef.current !== "saving") {
      reportSaveStatus("saving")
    }
    if (saveIdleTimer.current) clearTimeout(saveIdleTimer.current)
    saveIdleTimer.current = setTimeout(() => {
      reportSaveStatus("saved")
    }, COLLAB_SAVE_IDLE_MS)
  }, [reportSaveStatus])

  useEffect(() => {
    return () => {
      if (saveTimer.current) clearTimeout(saveTimer.current)
      if (saveIdleTimer.current) clearTimeout(saveIdleTimer.current)
    }
  }, [])

  useEffect(() => {
    if (!session.importRequired || importDone) return
    let cancelled = false
    void (async () => {
      reportSaveStatus("saving")
      try {
        const source = session.sourcePath || session.canonicalPath
        const buf = fetchSourceBytes
          ? await fetchSourceBytes(source)
          : await (await apiClient.getBlob(driveDownloadApiPath(source))).arrayBuffer()
        const content = await importFileToTipTap(source.split("/").pop() ?? "file.docx", buf)
        if (cancelled) return
        const payload = { source_path: source, content }
        if (importApi) {
          await importApi(payload)
        } else {
          await apiClient.post("/richtext/import", payload)
        }
        if (!cancelled) {
          setImportDone(true)
          reportSaveStatus("saved")
        }
      } catch {
        if (!cancelled) reportSaveStatus("error")
      }
    })()
    return () => {
      cancelled = true
    }
  }, [session, importDone, fetchSourceBytes, importApi, reportSaveStatus])

  useEffect(() => {
    if (!collaboration || !ydoc || !importDone) return

    setCollabSynced(false)
    setCollabError(null)

    const p = new HocuspocusProvider({
      url: session.wsUrl,
      name: session.roomId,
      token: session.token,
      document: ydoc,
      sessionAwareness: false,
      onSynced: () => setCollabSynced(true),
      onAuthenticationFailed: ({ reason }) => {
        setCollabError(reason ?? "Authentification collaboration refusée")
        setCollabSynced(false)
      },
    })
    setProvider(p)

    return () => {
      p.destroy()
      setProvider(null)
      setCollabSynced(false)
    }
  }, [collaboration, importDone, session.roomId, session.token, session.wsUrl, ydoc])

  const scheduleSave = useCallback(
    (json: Record<string, unknown>) => {
      if (!editable || collaboration) return
      if (saveTimer.current) clearTimeout(saveTimer.current)
      if (saveStatusRef.current !== "saving") {
        reportSaveStatus("saving")
      }
      saveTimer.current = setTimeout(() => {
        const doc = { schemaVersion: 1, editor: "tiptap", content: json }
        const body = JSON.stringify(doc)
        const savePromise = session.saveUrl
          ? fetch(session.saveUrl, {
              method: "PUT",
              headers: { "Content-Type": "application/json" },
              body,
            }).then((res) => {
              if (!res.ok) throw new Error("save failed")
            })
          : apiClient.put("/richtext/save", { path: session.canonicalPath, document: json })
        void savePromise
          .then(() => reportSaveStatus("saved"))
          .catch(() => reportSaveStatus("error"))
      }, SAVE_DEBOUNCE_MS)
    },
    [collaboration, editable, reportSaveStatus, session.canonicalPath, session.saveUrl]
  )

  const collabReady = !collaboration || (Boolean(provider) && collabSynced)
  const editorEnabled = importDone && collabReady

  const extensions = useMemo(
    () =>
      buildRichTextExtensions({
        collaboration: collaboration && ydoc ? { document: ydoc } : undefined,
        collaborationCaret:
          collaboration && provider
            ? { provider, user: { name: userName, color: userColor } }
            : undefined,
        editable,
      }),
    [collaboration, ydoc, provider, userName, userColor, editable]
  )

  const editor = useEditor(
    {
      immediatelyRender: false,
      editable,
      extensions,
      editorProps: {
        attributes: {
          class: RICHTEXT_EDITOR_CLASS,
        },
      },
      onUpdate: ({ editor: ed }) => {
        if (collaboration) {
          markCollabDirty()
          return
        }
        scheduleSave(ed.getJSON() as Record<string, unknown>)
      },
    },
    [editorEnabled, extensions, collaboration, markCollabDirty, scheduleSave]
  )

  useEffect(() => {
    if (!editor || editor.isDestroyed) return

    const syncSpellcheck = () => {
      if (editor.isDestroyed || !editor.isInitialized) return
      const dom = editor.view.dom
      dom.spellcheck = settings.spellcheck
      if (settings.spellcheck) {
        dom.setAttribute("spellcheck", "true")
        dom.removeAttribute("autocorrect")
        dom.removeAttribute("autocapitalize")
      } else {
        dom.setAttribute("spellcheck", "false")
        dom.setAttribute("autocorrect", "off")
        dom.setAttribute("autocapitalize", "off")
      }
    }

    syncSpellcheck()
    editor.on("create", syncSpellcheck)
    return () => {
      editor.off("create", syncSpellcheck)
    }
  }, [editor, settings.spellcheck])

  useEffect(() => {
    if (!editor || collaboration || !importDone || session.importRequired) return
    let cancelled = false
    void (async () => {
      try {
        let parsed: { content?: Record<string, unknown> }
        if (session.documentUrl) {
          const res = await fetch(session.documentUrl)
          if (!res.ok) throw new Error("load failed")
          parsed = JSON.parse(await res.text()) as { content?: Record<string, unknown> }
        } else {
          const blob = await apiClient.getBlob(driveDownloadApiPath(session.canonicalPath))
          parsed = JSON.parse(await blob.text()) as { content?: Record<string, unknown> }
        }
        if (!cancelled && parsed.content) editor.commands.setContent(parsed.content)
      } catch {
        /* blank */
      }
    })()
    return () => {
      cancelled = true
    }
  }, [editor, collaboration, importDone, session.canonicalPath, session.documentUrl, session.importRequired])

  if (collabError) {
    return (
      <div className="flex h-full items-center justify-center p-6 text-sm text-destructive">
        Collaboration indisponible : {collabError}
      </div>
    )
  }

  if (!editorEnabled || !editor) {
    const statusText =
      session.importRequired && !importDone
        ? "Import du document…"
        : collaboration && !collabSynced
          ? "Connexion à la collaboration…"
          : "Connexion…"
    return (
      <div className="flex h-full flex-col">
        {chrome && !settings.chromeCollapsed ? (
          <DocsChrome
            {...chrome}
            saveStatus={saveStatus}
            presenceUsers={presenceUsers}
            pageFormatId={settings.pageFormatId}
            onPageFormatChange={setPageFormatId}
            zoom={settings.zoom}
            onZoomChange={setZoom}
          />
        ) : null}
        <div className="flex flex-1 items-center justify-center text-sm text-muted-foreground">
          {statusText}
        </div>
      </div>
    )
  }

  return (
    <div className="flex h-full min-h-0 flex-col bg-white dark:bg-background">
      {chrome && !settings.chromeCollapsed ? (
        <DocsChrome
          {...chrome}
          saveStatus={saveStatus}
          presenceUsers={presenceUsers}
          pageFormatId={settings.pageFormatId}
          onPageFormatChange={setPageFormatId}
          zoom={settings.zoom}
          onZoomChange={setZoom}
        />
      ) : null}
      {editable ? (
        <DocsToolbar
          editor={editor}
          zoom={settings.zoom}
          onZoomChange={setZoom}
          spellcheck={settings.spellcheck}
          onToggleSpellcheck={toggleSpellcheck}
          showChromeToggle={Boolean(chrome)}
          chromeCollapsed={settings.chromeCollapsed}
          onToggleChromeCollapsed={toggleChromeCollapsed}
        />
      ) : null}
      {chrome ? (
        <DocsPageView
          editor={editor}
          pageFormatId={settings.pageFormatId}
          zoom={settings.zoom}
          editable={editable}
          onPageCountChange={handlePageCountChange}
        />
      ) : (
        <div className="min-h-0 flex-1 overflow-auto">
          <EditorContent editor={editor} className="h-full" />
        </div>
      )}
      {chrome ? (
        <DocsStatusBar pageFormatId={settings.pageFormatId} pageCount={pageCount} />
      ) : null}
    </div>
  )
}
