"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { apiClient } from "@/lib/api/client"
import Link from "next/link"
import { Button } from "@/components/ui/button"
import { ArrowLeft } from "lucide-react"
import { driveFolderHref } from "@/lib/drive/drive-sidebar-tree"
import { resolveDriveEditReturnTo } from "@/lib/drive/drive-url"

type DocEditorInstance = { destroyEditor: () => void }

declare global {
  interface Window {
    DocsAPI?: {
      DocEditor: new (id: string, config: Record<string, unknown>) => DocEditorInstance
    }
    DocEditor?: { instances: Record<string, DocEditorInstance | undefined> }
  }
}

let docsApiLoad: Promise<void> | null = null

function loadDocsApi(documentServerUrl: string): Promise<void> {
  if (window.DocsAPI) return Promise.resolve()
  if (docsApiLoad) return docsApiLoad

  const base = documentServerUrl.replace(/\/$/, "") + "/"
  docsApiLoad = new Promise((resolve, reject) => {
    const script = document.createElement("script")
    script.id = "onlyoffice-docs-api"
    script.src = `${base}web-apps/apps/api/documents/api.js`
    script.async = true
    script.onload = () => resolve()
    script.onerror = () => {
      docsApiLoad = null
      reject(new Error(`Error load DocsAPI from ${base}`))
    }
    document.body.appendChild(script)
  })

  return docsApiLoad
}

function destroyDocEditor(id: string) {
  const inst = window.DocEditor?.instances?.[id]
  if (inst) {
    try {
      inst.destroyEditor()
    } catch {
      /* ignore */
    }
    delete window.DocEditor!.instances[id]
  }
  document.getElementById(id)?.replaceChildren()
}

function OnlyOfficeMount({
  editorId,
  documentServerUrl,
  config,
  onError,
}: {
  editorId: string
  documentServerUrl: string
  config: Record<string, unknown>
  onError: (message: string) => void
}) {
  const configJson = JSON.stringify(config)
  const onErrorRef = useRef(onError)
  onErrorRef.current = onError

  useEffect(() => {
    let cancelled = false
    const id = editorId
    const parsed = JSON.parse(configJson) as Record<string, unknown>
    const editorConfig: Record<string, unknown> = {
      type: "desktop",
      width: "100%",
      height: "100%",
      events: {
        onDocumentReady: () => {
          /* loaded */
        },
        onError: (event: { data?: { errorCode?: number; errorDescription?: string } }) => {
          const code = event?.data?.errorCode
          const desc = event?.data?.errorDescription
          const msg =
            desc ||
            (code != null ? `OnlyOffice error ${code}` : "Erreur OnlyOffice.")
          onErrorRef.current(msg)
        },
      },
      ...parsed,
    }

    void loadDocsApi(documentServerUrl)
      .then(() => {
        if (cancelled) return
        if (!window.DocsAPI) throw new Error("DocsAPI is not defined")
        destroyDocEditor(id)
        if (!window.DocEditor) window.DocEditor = { instances: {} }
        const editor = new window.DocsAPI.DocEditor(id, editorConfig)
        window.DocEditor.instances[id] = editor
      })
      .catch((err: unknown) => {
        if (!cancelled) {
          onErrorRef.current(
            err instanceof Error ? err.message : "Impossible de charger OnlyOffice.",
          )
        }
      })

    return () => {
      cancelled = true
      destroyDocEditor(id)
    }
  }, [editorId, documentServerUrl, configJson])

  return <div id={editorId} className="h-full w-full min-h-0" />
}

export function OfficeEditor({
  filePath,
  returnTo,
}: {
  filePath: string
  returnTo?: string | null
}) {
  const instanceSeq = useRef(0)
  const [config, setConfig] = useState<Record<string, unknown> | null>(null)
  const [serverUrl, setServerUrl] = useState("")
  const [editorId, setEditorId] = useState<string | null>(null)
  const [error, setError] = useState<string | null>(null)
  const backHref = useMemo(
    () =>
      resolveDriveEditReturnTo(returnTo, filePath, (folderPath) =>
        driveFolderHref("files", folderPath)
      ),
    [returnTo, filePath]
  )
  const handleEditorError = useCallback((message: string) => {
    setError(message)
  }, [])

  useEffect(() => {
    let cancelled = false
    setConfig(null)
    setServerUrl("")
    setEditorId(null)
    setError(null)

    void (async () => {
      try {
        const res = await apiClient.post<{
          config: Record<string, unknown>
          serverUrl: string
        }>("/office/session", { path: filePath, mode: "edit" })
        if (cancelled) return
        instanceSeq.current += 1
        setConfig(res.config)
        setServerUrl(res.serverUrl || process.env.NEXT_PUBLIC_ONLYOFFICE_URL || "")
        setEditorId(`ultidrive-editor-${instanceSeq.current}`)
      } catch {
        if (!cancelled) setError("Impossible de charger l’éditeur.")
      }
    })()

    return () => {
      cancelled = true
    }
  }, [filePath])

  if (error) {
    return (
      <div className="flex h-dvh flex-col items-center justify-center gap-4">
        <p className="text-destructive">{error}</p>
        <Button asChild variant="outline">
          <Link href={backHref}>
            <ArrowLeft className="mr-2 h-4 w-4" />
            Retour
          </Link>
        </Button>
      </div>
    )
  }

  if (!config || !editorId || !serverUrl) {
    return <p className="p-8 text-center text-muted-foreground">Ouverture du document…</p>
  }

  const docServer = serverUrl.replace(/\/$/, "")

  return (
    <div className="flex h-dvh flex-col">
      <div className="flex h-12 shrink-0 items-center gap-2 border-b border-border px-3 ultidrive-editor-chrome">
        <Button variant="ghost" size="sm" asChild>
          <Link href={backHref}>
            <ArrowLeft className="mr-1 h-4 w-4" />
            Drive
          </Link>
        </Button>
        <span className="truncate text-sm font-medium">{filePath.split("/").pop()}</span>
      </div>
      <div className="relative min-h-0 flex-1">
        <OnlyOfficeMount
          editorId={editorId}
          documentServerUrl={docServer}
          config={config}
          onError={handleEditorError}
        />
      </div>
    </div>
  )
}
