"use client"

import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import type { Editor } from "@tiptap/react"
import { apiClient } from "@/lib/api/client"
import {
  notifyParagraphStyleCatalog,
  setActiveParagraphStyleCatalog,
} from "@/lib/drive/docs-paragraph-style-bridge"
import { buildParagraphStylesCss } from "@/lib/drive/docs-paragraph-styles-css"
import type { DocParagraphStyleDefinition, DocParagraphStylesCatalog } from "@/lib/drive/docs-paragraph-styles"
import {
  createCustomParagraphStyle,
  defaultDocumentParagraphStyles,
  emptyUserParagraphStyles,
  mergeParagraphStyleCatalogs,
} from "@/lib/drive/docs-paragraph-styles"
import type { DocParagraphStylesState } from "@/lib/drive/docs-styles-types"

const STYLES_DEBOUNCE_MS = 600

function parseCatalog(raw: unknown): DocParagraphStylesCatalog | null {
  if (!raw || typeof raw !== "object") return null
  const definitions = (raw as DocParagraphStylesCatalog).definitions
  if (!definitions || typeof definitions !== "object") return null
  return { definitions }
}

export function useDocsParagraphStyles({
  editor,
  initialDocumentStyles,
  editable,
  canonicalPath,
  saveUrl,
}: {
  editor: Editor | null
  initialDocumentStyles?: DocParagraphStylesCatalog | null
  editable?: boolean
  canonicalPath?: string
  saveUrl?: string
}) {
  const [documentStyles, setDocumentStyles] = useState<DocParagraphStylesCatalog>(
    () => initialDocumentStyles ?? defaultDocumentParagraphStyles()
  )
  const [userStyles, setUserStyles] = useState<DocParagraphStylesCatalog>(emptyUserParagraphStyles)
  const documentStylesRef = useRef(documentStyles)
  const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null)

  useEffect(() => {
    documentStylesRef.current = documentStyles
  }, [documentStyles])

  useEffect(() => {
    if (initialDocumentStyles) setDocumentStyles(initialDocumentStyles)
  }, [initialDocumentStyles])

  useEffect(() => {
    let cancelled = false
    void apiClient
      .get<DocParagraphStylesCatalog>("/richtext/user-paragraph-styles")
      .then((catalog) => {
        if (cancelled) return
        setUserStyles(parseCatalog(catalog) ?? emptyUserParagraphStyles())
      })
      .catch(() => {
        if (!cancelled) setUserStyles(emptyUserParagraphStyles())
      })
    return () => {
      cancelled = true
    }
  }, [])

  const mergedCatalog = useMemo(
    () => mergeParagraphStyleCatalogs(documentStyles, userStyles),
    [documentStyles, userStyles]
  )

  useEffect(() => {
    setActiveParagraphStyleCatalog(mergedCatalog)
    notifyParagraphStyleCatalog(mergedCatalog)
  }, [mergedCatalog])

  useEffect(() => {
    const css = buildParagraphStylesCss(mergedCatalog)
    const id = "docs-paragraph-styles-dynamic"
    let el = document.getElementById(id) as HTMLStyleElement | null
    if (!el) {
      el = document.createElement("style")
      el.id = id
      document.head.appendChild(el)
    }
    el.textContent = css
  }, [mergedCatalog])

  const flushDocumentStylesSave = useCallback(async () => {
    if (!editable || !canonicalPath) return
    const payload = { path: canonicalPath, paragraphStyles: documentStylesRef.current }
    if (saveUrl) {
      const res = await fetch(saveUrl, {
        method: "PUT",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(payload),
      })
      if (!res.ok) throw new Error("paragraph styles save failed")
      return
    }
    await apiClient.put("/richtext/save", payload)
  }, [canonicalPath, editable, saveUrl])

  const scheduleDocumentStylesSave = useCallback(() => {
    if (!editable) return
    if (saveTimer.current) clearTimeout(saveTimer.current)
    saveTimer.current = setTimeout(() => {
      void flushDocumentStylesSave().catch(() => undefined)
    }, STYLES_DEBOUNCE_MS)
  }, [editable, flushDocumentStylesSave])

  const saveUserStyles = useCallback(async (catalog: DocParagraphStylesCatalog) => {
    setUserStyles(catalog)
    await apiClient.put("/richtext/user-paragraph-styles", catalog)
  }, [])

  const updateDocumentStyle = useCallback(
    (styleId: string, definition: DocParagraphStyleDefinition) => {
      setDocumentStyles((prev) => ({
        definitions: {
          ...prev.definitions,
          [styleId]: { ...definition, id: styleId, scope: "document" },
        },
      }))
      scheduleDocumentStylesSave()
    },
    [scheduleDocumentStylesSave]
  )

  const updateUserStyle = useCallback(
    (styleId: string, definition: DocParagraphStyleDefinition) => {
      setUserStyles((prev) => {
        const next = {
          definitions: {
            ...prev.definitions,
            [styleId]: { ...definition, id: styleId, scope: "user" as const },
          },
        }
        void saveUserStyles(next)
        return next
      })
    },
    [saveUserStyles]
  )

  const createUserStyle = useCallback(
    (input: { name: string; basedOn?: string }) => {
      const template = mergedCatalog.definitions[input.basedOn ?? "normal"]
      const created = createCustomParagraphStyle({ ...input, template })
      setUserStyles((prev) => {
        const next = {
          definitions: { ...prev.definitions, [created.id]: created },
        }
        void saveUserStyles(next)
        return next
      })
      return created
    },
    [mergedCatalog.definitions, saveUserStyles]
  )

  useEffect(() => {
    if (!editor) return
    const onUpdate = ({
      styleId,
      definition,
    }: {
      styleId: string
      definition: DocParagraphStyleDefinition
    }) => {
      if (definition.scope === "user") updateUserStyle(styleId, definition)
      else updateDocumentStyle(styleId, definition)
    }
    editor.on("docsParagraphStyleUpdate", onUpdate)
    return () => {
      editor.off("docsParagraphStyleUpdate", onUpdate)
    }
  }, [editor, updateDocumentStyle, updateUserStyle])

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

  const state: DocParagraphStylesState = useMemo(
    () => ({ documentStyles, userStyles, mergedCatalog }),
    [documentStyles, mergedCatalog, userStyles]
  )

  return {
    state,
    updateDocumentStyle,
    updateUserStyle,
    createUserStyle,
    applyStyle: (styleId: string) => {
      editor?.commands.applyDocsParagraphStyle(styleId)
    },
    updateStyleFromSelection: (styleId: string) => {
      editor?.commands.updateDocsParagraphStyleFromSelection(styleId)
    },
  }
}
