"use client"

import { memo, useCallback, useEffect, useMemo, useRef, useState, type RefObject } from "react"
import { EditorContent, type Editor } from "@tiptap/react"
import type { DocPageLayout, DocPageSetup } from "@/lib/drive/doc-page-setup"
import {
  DocsHeaderFooterBand,
  type DocsHeaderFooterEditTarget,
  type DocsHeaderFooterRegion,
} from "@/components/drive/richtext/docs-header-footer-region"
import { DocsBodyMarginMasks } from "@/components/drive/richtext/docs-body-margin-masks"
import {
  DOCS_REGION_EDIT_EVENT,
  type DocsRegionEditDetail,
} from "@/lib/drive/docs-page-elements-bridge"
import {
  DOCS_CANVAS_PADDING_TOP_NARROW_PX,
  DOCS_CANVAS_PADDING_Y_PX,
  DOCS_PAGE_GAP_PX,
} from "@/lib/drive/docs-page-layout-constants"
import {
  effectiveMarginsPx,
} from "@/lib/drive/docs-header-footer-layout"
import {
  computePageCount,
  computePageMetrics,
  computeProseMinHeight,
  computeStackHeight,
} from "@/lib/drive/docs-page-metrics"
import { docsPageLengthToScreen, docsZoomToScale } from "@/lib/drive/docs-ruler-scale"
import { cn } from "@/lib/utils"
import { DocsGraphicSnapGuides } from "@/components/drive/richtext/docs-graphic-snap-guides"
import { DocsTableContextMenu } from "@/components/drive/richtext/docs-table-context-menu"
import { applyPageFlowLayout, computeSimulatedLayoutHeight, readPageFlowMetrics } from "@/lib/drive/extensions/docs-page-flow-decoration"
import { focusEditorAtPointer } from "@/lib/drive/focus-editor-at-pointer"

/** Total layout height inside ProseMirror (blocks + flow spacers). */
function measureProseContentHeight(prose: HTMLElement): number {
  const metrics = readPageFlowMetrics(prose)
  if (!metrics) return 0

  const blocks: Array<{ height: number }> = []
  for (const child of prose.children) {
    const el = child as HTMLElement
    if (el.classList.contains("docs-page-flow-spacer")) continue
    const style = getComputedStyle(el)
    const marginBottom = parseFloat(style.marginBottom) || 0
    blocks.push({ height: el.offsetHeight + marginBottom })
  }

  if (blocks.length === 0) return 0

  const simulated = computeSimulatedLayoutHeight(
    blocks,
    metrics.bodyAreaH,
    metrics.interPageSpacer
  )

  let maxBottom = 0
  for (const child of prose.children) {
    const el = child as HTMLElement
    maxBottom = Math.max(maxBottom, el.offsetTop + el.offsetHeight)
  }

  return Math.max(simulated, maxBottom)
}

function DocsPageViewInner({
  editor,
  pageLayout,
  zoom,
  editable,
  showLayout,
  showNonPrintableChars,
  editorMode,
  canvasRef: canvasRefProp,
  onPageCountChange,
  onNarrowViewportChange,
  onCanvasHeightChange,
  onRegionContentChange,
  onPageSetupChange,
  onRegionEditorChange,
}: {
  editor: Editor
  pageLayout: DocPageLayout
  zoom: number
  editable: boolean
  showLayout: boolean
  showRuler: boolean
  showNonPrintableChars: boolean
  editorMode: "edit" | "suggest" | "view"
  canvasRef?: RefObject<HTMLDivElement | null>
  onPageCountChange?: (count: number) => void
  onNarrowViewportChange?: (narrow: boolean) => void
  onCanvasHeightChange?: (height: number) => void
  onRegionContentChange?: (
    region: DocsHeaderFooterRegion,
    content: Record<string, unknown>,
    meta: { pageIndex: number; contentHeightPx: number }
  ) => void
  onPageSetupChange?: (patch: Partial<DocPageSetup>) => void
  onRegionEditorChange?: (editor: Editor | null) => void
}) {
  const pageWidth = pageLayout.widthPx
  const pageHeight = pageLayout.heightPx
  const margins = pageLayout.marginsPx

  const [pageCount, setPageCount] = useState(1)
  const [narrowViewport, setNarrowViewport] = useState(false)
  const [editingTarget, setEditingTarget] = useState<DocsHeaderFooterEditTarget>(null)
  const [pageRegionHeights, setPageRegionHeights] = useState<
    Record<string, number>
  >({})

  const handleRegionHeightMeasure = useCallback(
    (payload: {
      region: DocsHeaderFooterRegion
      pageIndex: number
      heightPx: number
    }) => {
      const key = `${payload.region}-${payload.pageIndex}`
      setPageRegionHeights((prev) => {
        if (prev[key] === payload.heightPx) return prev
        return { ...prev, [key]: payload.heightPx }
      })
    },
    []
  )

  const measuredRegionHeights = useMemo(() => {
    let header = 0
    let footer = 0
    for (const [key, heightPx] of Object.entries(pageRegionHeights)) {
      if (key.startsWith("header-")) header = Math.max(header, heightPx)
      if (key.startsWith("footer-")) footer = Math.max(footer, heightPx)
    }
    return {
      ...(header > 0 ? { header } : {}),
      ...(footer > 0 ? { footer } : {}),
    }
  }, [pageRegionHeights])

  const effectiveMargins = useMemo(
    () =>
      effectiveMarginsPx(
        pageLayout,
        null,
        editingTarget ? undefined : measuredRegionHeights
      ),
    [pageLayout, editingTarget, measuredRegionHeights]
  )
  const metrics = useMemo(
    () => computePageMetrics({ ...pageLayout, effectiveMarginsPx: effectiveMargins }),
    [pageLayout, effectiveMargins]
  )

  const localCanvasRef = useRef<HTMLDivElement>(null)
  const canvasRef = canvasRefProp ?? localCanvasRef
  const contentRef = useRef<HTMLDivElement>(null)
  const onPageCountChangeRef = useRef(onPageCountChange)
  onPageCountChangeRef.current = onPageCountChange

  const scale = docsZoomToScale(zoom)
  const scaledWidth = docsPageLengthToScreen(pageWidth, scale)

  const stopRegionEdit = useCallback(() => {
    setEditingTarget(null)
    onRegionEditorChange?.(null)
  }, [onRegionEditorChange])

  const startRegionEdit = useCallback(
    (region: DocsHeaderFooterRegion, pageIndex: number) => {
      setEditingTarget({ region, pageIndex })
    },
    []
  )

  const handleRegionEditorReady = useCallback(
    (editor: Editor | null) => {
      onRegionEditorChange?.(editor)
    },
    [onRegionEditorChange]
  )

  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      if (event.key === "Escape" && editingTarget) {
        event.preventDefault()
        stopRegionEdit()
      }
    }
    window.addEventListener("keydown", onKey)
    return () => window.removeEventListener("keydown", onKey)
  }, [editingTarget, stopRegionEdit])

  useEffect(() => {
    const onRegionEditRequest = (event: Event) => {
      const detail = (event as CustomEvent<DocsRegionEditDetail>).detail
      if (!detail?.region) return
      startRegionEdit(detail.region, detail.pageIndex ?? 0)
    }
    window.addEventListener(DOCS_REGION_EDIT_EVENT, onRegionEditRequest)
    return () => window.removeEventListener(DOCS_REGION_EDIT_EVENT, onRegionEditRequest)
  }, [startRegionEdit])

  useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas) return
    const syncViewport = () => {
      const narrow = canvas.clientWidth < scaledWidth
      setNarrowViewport(narrow)
      onNarrowViewportChange?.(narrow)
      onCanvasHeightChange?.(canvas.clientHeight)
    }
    syncViewport()
    const ro = new ResizeObserver(syncViewport)
    ro.observe(canvas)
    return () => ro.disconnect()
  }, [onCanvasHeightChange, onNarrowViewportChange, scaledWidth, canvasRef])

  useEffect(() => {
    if (!showLayout) return
    const surface = contentRef.current
    if (!surface) return

    let debounceId: ReturnType<typeof setTimeout> | null = null
    let cancelled = false

    const measurePageCount = () => {
      const prose = surface.querySelector(".ProseMirror") as HTMLElement | null
      if (!prose) return
      const contentHeight = measureProseContentHeight(prose)
      const count = computePageCount(contentHeight, metrics)
      setPageCount((prev) => (prev === count ? prev : count))
    }

    const runLayoutPasses = (passesLeft: number) => {
      if (cancelled || editor.isDestroyed) return
      requestAnimationFrame(() => {
        if (cancelled || editor.isDestroyed) return
        const changed = applyPageFlowLayout(editor)
        if (changed && passesLeft > 1) {
          runLayoutPasses(passesLeft - 1)
          return
        }
        measurePageCount()
      })
    }

    let flushPending = false
    const scheduleLayout = () => {
      if (!flushPending) {
        flushPending = true
        requestAnimationFrame(() => {
          flushPending = false
          runLayoutPasses(2)
        })
      }
      if (debounceId) clearTimeout(debounceId)
      debounceId = setTimeout(() => {
        debounceId = null
        runLayoutPasses(2)
      }, 32)
    }

    scheduleLayout()

    const onTransaction = ({ transaction }: { transaction: { docChanged: boolean } }) => {
      if (!transaction.docChanged) return
      scheduleLayout()
    }
    editor.on("transaction", onTransaction)

    return () => {
      cancelled = true
      if (debounceId) clearTimeout(debounceId)
      editor.off("transaction", onTransaction)
    }
  }, [editor, metrics, pageRegionHeights, showLayout])

  useEffect(() => {
    onPageCountChangeRef.current?.(pageCount)
  }, [pageCount])

  const stackHeight = computeStackHeight(pageCount, pageHeight)
  const proseMinHeight = computeProseMinHeight(pageCount, metrics)

  const scaledHeight = docsPageLengthToScreen(stackHeight, scale)
  const verticalPaddingTop = narrowViewport
    ? DOCS_CANVAS_PADDING_TOP_NARROW_PX
    : DOCS_CANVAS_PADDING_Y_PX
  const verticalPaddingBottom = DOCS_CANVAS_PADDING_Y_PX

  const textAreaBorderCss = pageLayout.textAreaBorderCss
  const sheetBorderCss = pageLayout.sheetBorderCss
  const pageBackground = pageLayout.pageColor
  const backgroundLayers = pageLayout.pageBackgroundLayers

  const renderPageBackground = (index: number) => (
    <>
      {backgroundLayers?.gradientCss ? (
        <div
          key={`bg-gradient-${index}`}
          className="pointer-events-none absolute inset-0 z-[1]"
          style={{ background: backgroundLayers.gradientCss }}
          aria-hidden
        />
      ) : null}
      {backgroundLayers?.fillImageStyle ? (
        <div
          key={`bg-image-${index}`}
          className="pointer-events-none absolute inset-0 z-[1]"
          style={backgroundLayers.fillImageStyle}
          aria-hidden
        />
      ) : null}
      {backgroundLayers?.watermarkStyle ? (
        <div
          key={`bg-watermark-${index}`}
          className="pointer-events-none absolute inset-0 z-[2] flex items-center justify-center overflow-hidden"
          aria-hidden
        >
          {backgroundLayers.watermarkStyle.imageSrc ? (
            <img
              src={backgroundLayers.watermarkStyle.imageSrc}
              alt=""
              className="max-h-[70%] max-w-[70%] select-none object-contain"
              style={{
                opacity: backgroundLayers.watermarkStyle.opacity,
                transform: `rotate(${backgroundLayers.watermarkStyle.rotationDeg}deg)`,
              }}
            />
          ) : (
            <span
              className="select-none whitespace-nowrap text-[72px] font-light leading-none"
              style={{
                color: backgroundLayers.watermarkStyle.color,
                opacity: backgroundLayers.watermarkStyle.opacity,
                transform: `rotate(${backgroundLayers.watermarkStyle.rotationDeg}deg)`,
              }}
            >
              {backgroundLayers.watermarkStyle.text}
            </span>
          )}
        </div>
      ) : null}
    </>
  )

  const bodyDimmed = editingTarget != null

  return (
    <div ref={canvasRef} className={cn(
      "ultidrive-docs-canvas h-full min-h-0 overflow-auto",
      showLayout ? "bg-[#f9fbfd] dark:bg-[#202124]" : "bg-white dark:bg-background",
      showNonPrintableChars && "docs-show-non-printable",
      editorMode === "suggest" && "docs-editor-mode-suggest",
      editorMode === "view" && "docs-editor-mode-view"
    )}>
      <div
        className="mx-auto"
        style={{
          width: scaledWidth,
          paddingTop: verticalPaddingTop,
          paddingBottom: verticalPaddingBottom,
          minHeight: (showLayout ? scaledHeight : proseMinHeight + margins.top + margins.bottom) + verticalPaddingTop + verticalPaddingBottom,
        }}
      >
        <div
          className="relative mx-auto overflow-visible"
          style={{ width: scaledWidth, height: showLayout ? scaledHeight : undefined }}
        >
          <div
            data-docs-page-stack
            data-docs-page-height={pageHeight}
            data-docs-page-width={pageWidth}
            data-docs-page-scale={scale}
            data-docs-page-margin-top={effectiveMargins.top}
            data-docs-page-margin-right={effectiveMargins.right}
            data-docs-page-margin-bottom={effectiveMargins.bottom}
            data-docs-page-margin-left={effectiveMargins.left}
            className="absolute left-1/2 top-0 -translate-x-1/2 overflow-visible"
            style={{
              width: pageWidth,
              height: stackHeight,
              transform: `scale(${scale})`,
              transformOrigin: "top center",
            }}
          >
            {showLayout
              ? Array.from({ length: pageCount }, (_, index) => {
                  const pageTop = index * (pageHeight + DOCS_PAGE_GAP_PX)
                  return (
                    <div
                      key={index}
                      className={cn(
                        "ultidrive-docs-page absolute left-0 overflow-hidden dark:bg-white",
                        sheetBorderCss && "ultidrive-docs-page--imported-border"
                      )}
                      style={{
                        top: pageTop,
                        width: pageWidth,
                        height: pageHeight,
                        backgroundColor: pageBackground,
                        boxShadow:
                          "0 1px 3px 1px rgba(60,64,67,.15), 0 1px 2px 0 rgba(60,64,67,.3)",
                        ...(sheetBorderCss
                          ? {
                              borderTop: sheetBorderCss.top ?? "none",
                              borderRight: sheetBorderCss.right ?? "none",
                              borderBottom: sheetBorderCss.bottom ?? "none",
                              borderLeft: sheetBorderCss.left ?? "none",
                            }
                          : {}),
                      }}
                      aria-hidden
                    >
                      {renderPageBackground(index)}
                    </div>
                  )
                })
              : null}

            {showLayout && textAreaBorderCss
              ? Array.from({ length: pageCount }, (_, index) => (
                  <div
                    key={`text-border-${index}`}
                    className="pointer-events-none absolute z-[5] box-border"
                    style={{
                      top: index * (pageHeight + DOCS_PAGE_GAP_PX) + effectiveMargins.top,
                      left: effectiveMargins.left,
                      width: pageWidth - effectiveMargins.left - effectiveMargins.right,
                      height: pageHeight - effectiveMargins.top - effectiveMargins.bottom,
                      borderTop: textAreaBorderCss.top ?? "none",
                      borderRight: textAreaBorderCss.right ?? "none",
                      borderBottom: textAreaBorderCss.bottom ?? "none",
                      borderLeft: textAreaBorderCss.left ?? "none",
                    }}
                    aria-hidden
                  />
                ))
              : null}

            {showLayout ? (
              <DocsBodyMarginMasks
                pageCount={pageCount}
                pageLayout={pageLayout}
                pageWidth={pageWidth}
                pageHeight={pageHeight}
                pageColor={pageBackground}
                pageRegionHeights={pageRegionHeights}
              />
            ) : null}

            {showLayout
              ? Array.from({ length: pageCount }, (_, index) => {
                  const pageTop = index * (pageHeight + DOCS_PAGE_GAP_PX)
                  return (
                    <div key={`hf-${index}`}>
                      <DocsHeaderFooterBand
                        region="header"
                        pageLayout={pageLayout}
                        pageIndex={index}
                        pageTop={pageTop}
                        pageWidth={pageWidth}
                        pageHeight={pageHeight}
                        margins={margins}
                        editable={editable}
                        editingTarget={editingTarget}
                        onStartEdit={startRegionEdit}
                        onStopEdit={stopRegionEdit}
                        onContentChange={(region, content, meta) =>
                          onRegionContentChange?.(region, content, meta)
                        }
                        onPageSetupChange={(patch) => onPageSetupChange?.(patch)}
                        onRegionHeightMeasure={handleRegionHeightMeasure}
                        onRegionEditorReady={
                          editingTarget?.region === "header" &&
                          editingTarget.pageIndex === index
                            ? handleRegionEditorReady
                            : undefined
                        }
                      />
                      <DocsHeaderFooterBand
                        region="footer"
                        pageLayout={pageLayout}
                        pageIndex={index}
                        pageTop={pageTop}
                        pageWidth={pageWidth}
                        pageHeight={pageHeight}
                        margins={margins}
                        editable={editable}
                        editingTarget={editingTarget}
                        onStartEdit={startRegionEdit}
                        onStopEdit={stopRegionEdit}
                        onContentChange={(region, content, meta) =>
                          onRegionContentChange?.(region, content, meta)
                        }
                        onPageSetupChange={(patch) => onPageSetupChange?.(patch)}
                        onRegionHeightMeasure={handleRegionHeightMeasure}
                        onRegionEditorReady={
                          editingTarget?.region === "footer" &&
                          editingTarget.pageIndex === index
                            ? handleRegionEditorReady
                            : undefined
                        }
                      />
                    </div>
                  )
                })
              : null}

            {bodyDimmed ? (
              <div
                className="pointer-events-none absolute inset-0 z-[12] bg-[#e8eaed]/40"
                style={{ height: stackHeight }}
                aria-hidden
              />
            ) : null}

            <div
              id="docs-page-graphic-layer-behind"
              className="pointer-events-none absolute left-0 top-0 z-[12]"
              style={{ width: pageWidth, height: stackHeight }}
            />

            <div
              id="docs-page-graphic-layer-front"
              className="pointer-events-none absolute left-0 top-0 z-[18]"
              style={{ width: pageWidth, height: stackHeight }}
            />

            <DocsGraphicSnapGuides pageWidth={pageWidth} pageHeight={pageHeight} />

            <div
              ref={contentRef}
              className={cn(
                "ultidrive-docs-editor-surface relative",
                showLayout && "ultidrive-docs-editor-surface--paginated",
                !showLayout && "ultidrive-docs-editor-surface--compact",
                bodyDimmed && "ultidrive-docs-editor-surface--dimmed"
              )}
              style={{
                padding: `${effectiveMargins.top}px ${effectiveMargins.right}px ${effectiveMargins.bottom}px ${effectiveMargins.left}px`,
                height: showLayout ? stackHeight : undefined,
                minHeight: showLayout
                  ? undefined
                  : proseMinHeight + effectiveMargins.top + effectiveMargins.bottom,
                ["--docs-stack-height" as string]: `${stackHeight}px`,
                ["--docs-prose-min-height" as string]: `${proseMinHeight}px`,
                ["--docs-body-area-h" as string]: `${metrics.bodyAreaHeight}px`,
                ["--docs-inter-page-spacer" as string]: `${metrics.interPageSpacer}px`,
              }}
              onMouseDown={(event) => {
                if (bodyDimmed) {
                  const target = event.target as HTMLElement
                  if (
                    !target.closest(".docs-hf-band") &&
                    !target.closest(".docs-hf-chrome")
                  ) {
                    stopRegionEdit()
                  }
                  return
                }
                if (!editable || editingTarget) return
                const target = event.target as HTMLElement
                if (target.closest(".ProseMirror")) return
                event.preventDefault()
                focusEditorAtPointer(editor, event.clientX, event.clientY)
              }}
            >
              <DocsTableContextMenu editor={editor} disabled={!editable || bodyDimmed}>
                <div className="min-h-0 min-w-0">
                  <EditorContent
                    editor={editor}
                    className={cn(!editable && "pointer-events-none select-text")}
                  />
                </div>
              </DocsTableContextMenu>
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

export const DocsPageView = memo(DocsPageViewInner)

export function DocsStatusBar({
  pageLayout,
  pageCount,
  currentPage = 1,
  className,
}: {
  pageLayout: DocPageLayout
  pageCount: number
  currentPage?: number
  className?: string
}) {
  const pageLabel = Math.min(Math.max(1, currentPage), Math.max(1, pageCount))

  return (
    <div
      className={cn(
        "docs-status-bar flex shrink-0 items-center justify-between border-t border-[#dadce0] bg-[#edf2fa] px-4 py-1 text-xs text-[#5f6368] dark:border-border dark:bg-muted/40 dark:text-muted-foreground",
        className
      )}
    >
      <span>
        Page {pageLabel} sur {pageCount}
      </span>
      <span>
        {pageLayout.format.label} ({pageLayout.format.widthMm} × {pageLayout.format.heightMm} mm)
      </span>
    </div>
  )
}
