import { Extension } from "@tiptap/core"
import type { Editor } from "@tiptap/react"
import { Plugin, PluginKey } from "@tiptap/pm/state"
import { Decoration, DecorationSet, type EditorView } from "@tiptap/pm/view"

export const PAGE_FLOW_PLUGIN_KEY = new PluginKey<DecorationSet>("docsPageFlowDecoration")

function decorationSetsEqual(
  current: DecorationSet | undefined,
  next: DecorationSet
): boolean {
  if (!current || current === DecorationSet.empty) {
    return next === DecorationSet.empty || next.find(0, Number.MAX_SAFE_INTEGER).length === 0
  }
  const a = current.find(0, Number.MAX_SAFE_INTEGER)
  const b = next.find(0, Number.MAX_SAFE_INTEGER)
  if (a.length !== b.length) return false
  for (let i = 0; i < a.length; i++) {
    if (a[i].from !== b[i].from) return false
    const aPush = (a[i].spec as { pushPx?: number }).pushPx
    const bPush = (b[i].spec as { pushPx?: number }).pushPx
    if (aPush !== bPush) return false
  }
  return true
}

export function readPageFlowMetrics(prose: HTMLElement): {
  bodyAreaH: number
  interPageSpacer: number
} | null {
  const surface = prose.closest(".ultidrive-docs-editor-surface")
  if (!surface) return null
  const styles = getComputedStyle(surface)
  const bodyAreaH = parseFloat(styles.getPropertyValue("--docs-body-area-h"))
  const interPageSpacer = parseFloat(styles.getPropertyValue("--docs-inter-page-spacer"))
  if (!Number.isFinite(bodyAreaH) || bodyAreaH <= 0) return null
  if (!Number.isFinite(interPageSpacer) || interPageSpacer <= 0) return null
  return { bodyAreaH, interPageSpacer }
}

function blockDomAtOffset(view: EditorView, offset: number): HTMLElement | null {
  const dom = view.nodeDOM(offset)
  if (dom instanceof HTMLElement) {
    if (dom.parentElement === view.dom) return dom
    let el: HTMLElement | null = dom
    while (el && el.parentElement !== view.dom) el = el.parentElement
    return el
  }
  return null
}

/** Block height in prose px (offsetHeight + bottom margin; immune to canvas scale). */
function measureBlockFlowHeight(dom: HTMLElement): number {
  const style = getComputedStyle(dom)
  const marginBottom = parseFloat(style.marginBottom) || 0
  return dom.offsetHeight + marginBottom
}

function createSpacerElement(height: number): HTMLElement {
  const el = document.createElement("div")
  el.className = "docs-page-flow-spacer"
  el.style.cssText = `display:block;width:100%;height:${height}px;margin:0;padding:0;border:0;flex-shrink:0`
  el.setAttribute("aria-hidden", "true")
  el.contentEditable = "false"
  return el
}

/** Simulate vertical flow without reading pushed DOM positions (avoids clearing decorations). */
export function computePageFlowPushes(
  blocks: Array<{ height: number }>,
  bodyAreaH: number,
  interPageSpacer: number
): Array<{ blockIndex: number; pushPx: number; breakY: number }> {
  const pageStep = bodyAreaH + interPageSpacer
  const pushes: Array<{ blockIndex: number; pushPx: number; breakY: number }> = []
  let simulatedY = 0
  let nextBreakY = bodyAreaH

  blocks.forEach(({ height: blockH }, blockIndex) => {
    if (blockH <= 0) return

    while (simulatedY + blockH > nextBreakY) {
      if (simulatedY < nextBreakY) {
        const pushPx = nextBreakY - simulatedY + interPageSpacer
        pushes.push({ blockIndex, pushPx, breakY: nextBreakY })
        simulatedY = nextBreakY + interPageSpacer
        nextBreakY += pageStep
        break
      }
      nextBreakY += pageStep
    }

    simulatedY += blockH
  })

  return pushes
}

export function computeSimulatedLayoutHeight(
  blocks: Array<{ height: number }>,
  bodyAreaH: number,
  interPageSpacer: number
): number {
  const pageStep = bodyAreaH + interPageSpacer
  let simulatedY = 0
  let nextBreakY = bodyAreaH

  for (const { height: blockH } of blocks) {
    if (blockH <= 0) continue
    while (simulatedY + blockH > nextBreakY) {
      if (simulatedY < nextBreakY) {
        simulatedY = nextBreakY + interPageSpacer
        nextBreakY += pageStep
        break
      }
      nextBreakY += pageStep
    }
    simulatedY += blockH
  }

  return simulatedY
}

function collectFlowBlocks(view: EditorView): Array<{ height: number; offset: number }> {
  const blocks: Array<{ height: number; offset: number }> = []
  view.state.doc.forEach((node, offset) => {
    if (!node.isBlock || node.type.name === "doc") return
    const dom = blockDomAtOffset(view, offset)
    if (!dom) return
    const blockH = measureBlockFlowHeight(dom)
    if (blockH <= 0) return
    blocks.push({ height: blockH, offset })
  })
  return blocks
}

/** Build page-flow spacer widgets for blocks that cross a page body boundary. */
export function buildPageFlowDecorations(view: EditorView): DecorationSet {
  const metrics = readPageFlowMetrics(view.dom)
  if (!metrics) return DecorationSet.empty

  const { bodyAreaH, interPageSpacer } = metrics
  const blocks = collectFlowBlocks(view)

  const pushes = computePageFlowPushes(
    blocks.map((b) => ({ height: b.height })),
    bodyAreaH,
    interPageSpacer
  )

  const decorations = pushes.map(({ blockIndex, pushPx, breakY }) => {
    const block = blocks[blockIndex]
    return Decoration.widget(
      block.offset,
      () => createSpacerElement(pushPx),
      { side: -1, key: `page-flow-spacer-${block.offset}-${breakY}`, pushPx }
    )
  })

  return DecorationSet.create(view.state.doc, decorations)
}

/** Apply page-flow layout once. Returns true if decorations changed. */
export function applyPageFlowLayout(editor: Editor): boolean {
  if (editor.isDestroyed || !editor.isInitialized) return false
  const view = editor.view
  const next = buildPageFlowDecorations(view)
  const current = PAGE_FLOW_PLUGIN_KEY.getState(view.state)
  if (decorationSetsEqual(current, next)) return false
  const tr = view.state.tr.setMeta(PAGE_FLOW_PLUGIN_KEY, next)
  tr.setMeta("addToHistory", false)
  view.dispatch(tr)
  return true
}

/** Pure helper for tests: return count of inter-page pushes. */
export function countPageFlowSpacers(
  blocks: Array<{ height: number }>,
  bodyAreaH: number,
  interPageSpacer: number
): number {
  return computePageFlowPushes(blocks, bodyAreaH, interPageSpacer).length
}

/** State-only plugin: layout is driven from docs-page-view (no view plugin / observers). */
export const DocsPageFlowDecoration = Extension.create({
  name: "docsPageFlowDecoration",

  addProseMirrorPlugins() {
    return [
      new Plugin({
        key: PAGE_FLOW_PLUGIN_KEY,
        state: {
          init: () => DecorationSet.empty,
          apply(tr, set) {
            const next = tr.getMeta(PAGE_FLOW_PLUGIN_KEY)
            if (next instanceof DecorationSet) return next
            return set.map(tr.mapping, tr.doc)
          },
        },
        props: {
          decorations(state) {
            return PAGE_FLOW_PLUGIN_KEY.getState(state) ?? DecorationSet.empty
          },
        },
      }),
    ]
  },
})
