import mammoth from "mammoth"
import {
  normalizeImportedGraphics,
  normalizeLegacyImageAttrs,
} from "@/lib/drive/docs-graphic-import"
import { htmlTableToTipTap, normalizeImportedTables } from "@/lib/drive/docs-table-import"
import { prepareTablesForDocxExport } from "@/lib/drive/docs-table-export"
import { enrichContentFromDocxDrawings } from "@/lib/drive/docx-drawing-import"
import { enrichGraphicsFromDocxPositions } from "@/lib/drive/docx-position-import"
import { extractDocxHeaderFooter } from "@/lib/drive/docx-header-footer-import"
import {
  extractDocxPageSetup,
  type DocPageSetup,
} from "@/lib/drive/doc-page-setup"
import { ensureMinimalTipTapDoc } from "@/lib/drive/richtext-content"

export type TipTapJSON = Record<string, unknown>

export type RichTextImportResult = {
  content: TipTapJSON
  pageSetup?: DocPageSetup | null
}

function imageNodeFromElement(el: HTMLElement): TipTapJSON | null {
  const src = el.getAttribute("src")
  if (!src) return null
  const attrs: Record<string, unknown> = {
    src,
    alt: el.getAttribute("alt") ?? "",
  }
  const title = el.getAttribute("title")
  if (title) attrs.title = title
  const width = el.getAttribute("width")
  const height = el.getAttribute("height")
  if (width) attrs.width = Number(width) || width
  if (height) attrs.height = Number(height) || height
  return { type: "image", attrs }
}

function inlineContentFromElement(el: HTMLElement): TipTapJSON[] {
  const content: TipTapJSON[] = []
  for (const node of el.childNodes) {
    if (node.nodeType === Node.TEXT_NODE) {
      const text = node.textContent ?? ""
      if (text) content.push({ type: "text", text })
      continue
    }
    if (node.nodeType !== Node.ELEMENT_NODE) continue
    const child = node as HTMLElement
    const tag = child.tagName.toLowerCase()
    if (tag === "img") {
      const image = imageNodeFromElement(child)
      if (image) content.push(image)
      continue
    }
    content.push(...inlineContentFromElement(child))
  }
  return content
}

/** Preserve graphic positioning attrs, then upgrade to docsGraphic nodes. */
export function normalizeImportedTipTap(content: TipTapJSON): TipTapJSON {
  const withLegacy = normalizeLegacyImageAttrs(content)
  const withTables = normalizeImportedTables(withLegacy)
  return normalizeImportedGraphics(withTables)
}

function htmlToTipTapDoc(html: string): TipTapJSON {
  const parser = typeof DOMParser !== "undefined" ? new DOMParser() : null
  if (!parser) {
    return {
      type: "doc",
      content: [{ type: "paragraph", content: [{ type: "text", text: html.replace(/<[^>]+>/g, " ") }] }],
    }
  }
  const doc = parser.parseFromString(html, "text/html")
  const blocks: TipTapJSON[] = []

  const walk = (node: Node) => {
    if (node.nodeType === Node.TEXT_NODE) {
      const text = node.textContent ?? ""
      if (text.trim()) {
        blocks.push({ type: "paragraph", content: [{ type: "text", text }] })
      }
      return
    }
    if (node.nodeType !== Node.ELEMENT_NODE) return
    const el = node as HTMLElement
    const tag = el.tagName.toLowerCase()
    if (tag === "img") {
      const image = imageNodeFromElement(el)
      if (image) {
        blocks.push({ type: "paragraph", content: [image] })
      }
      return
    }
    if (tag === "p" || tag === "div") {
      const content = inlineContentFromElement(el)
      blocks.push({
        type: "paragraph",
        content: content.length ? content : [],
      })
      return
    }
    if (/^h[1-6]$/.test(tag)) {
      const level = Number(tag[1])
      blocks.push({
        type: "heading",
        attrs: { level },
        content: [{ type: "text", text: el.textContent ?? "" }],
      })
      return
    }
    if (tag === "ul" || tag === "ol") {
      const listType = tag === "ul" ? "bulletList" : "orderedList"
      const items = Array.from(el.querySelectorAll(":scope > li")).map((li) => ({
        type: "listItem",
        content: [{ type: "paragraph", content: inlineContentFromElement(li as HTMLElement) }],
      }))
      blocks.push({ type: listType, content: items })
      return
    }
    if (tag === "table") {
      blocks.push(htmlTableToTipTap(el as HTMLTableElement))
      return
    }
    Array.from(el.childNodes).forEach(walk)
  }

  Array.from(doc.body.childNodes).forEach(walk)
  if (blocks.length === 0) {
    blocks.push({ type: "paragraph" })
  }
  return normalizeImportedTipTap({ type: "doc", content: blocks })
}

export async function importDocxToTipTap(buffer: ArrayBuffer): Promise<RichTextImportResult> {
  const pageSetup = await extractDocxPageSetup(buffer)
  const headerFooter = await extractDocxHeaderFooter(buffer)
  const normalizeRegion = (region: typeof headerFooter.header) => {
    if (!region?.content) return region
    return { ...region, content: normalizeImportedTipTap(region.content as TipTapJSON) }
  }
  const mergedPageSetup: DocPageSetup | null = pageSetup
    ? {
        ...pageSetup,
        header: normalizeRegion(headerFooter.header ?? pageSetup.header ?? null),
        footer: normalizeRegion(headerFooter.footer ?? pageSetup.footer ?? null),
        headerFooterDifferentFirstPage:
          headerFooter.headerFooterDifferentFirstPage ??
          pageSetup.headerFooterDifferentFirstPage ??
          false,
      }
    : headerFooter.header || headerFooter.footer
      ? {
          widthMm: 210,
          heightMm: 297,
          marginsMm: { top: 25.4, right: 25.4, bottom: 25.4, left: 25.4 },
          header: normalizeRegion(headerFooter.header ?? null),
          footer: normalizeRegion(headerFooter.footer ?? null),
          headerFooterDifferentFirstPage: headerFooter.headerFooterDifferentFirstPage ?? false,
        }
      : null

  try {
    const { parseDOCX } = await import("@docen/import-docx")
    const content = await parseDOCX(buffer, { image: { crop: true } })
    if (content && typeof content === "object") {
      let normalized = normalizeImportedTipTap(content as TipTapJSON)
      normalized = await enrichGraphicsFromDocxPositions(buffer, normalized)
      normalized = await enrichContentFromDocxDrawings(buffer, normalized)
      return { content: ensureMinimalTipTapDoc(normalized), pageSetup: mergedPageSetup }
    }
  } catch (error) {
    if (process.env.NODE_ENV !== "production") {
      console.warn("[richtext-import] parseDOCX failed, falling back to mammoth", error)
    }
  }
  const result = await mammoth.convertToHtml(
    { arrayBuffer: buffer },
    {
      convertImage: mammoth.images.imgElement((image) =>
        image.read("base64").then((imageBuffer) => ({
          src: `data:${image.contentType};base64,${imageBuffer}`,
        }))
      ),
    }
  )
  let content = htmlToTipTapDoc(result.value)
  content = await enrichGraphicsFromDocxPositions(buffer, content)
  content = await enrichContentFromDocxDrawings(buffer, content)
  return { content: ensureMinimalTipTapDoc(content), pageSetup: mergedPageSetup }
}

export async function exportTipTapToDocx(content: TipTapJSON): Promise<Blob> {
  try {
    const { generateDOCX } = await import("@docen/export-docx")
    const prepared = prepareTablesForDocxExport(content)
    const buf = await generateDOCX(prepared, { outputType: "blob" })
    if (buf instanceof Blob) return buf
  } catch {
    /* fallback unavailable */
  }
  throw new Error("Export DOCX indisponible")
}

export async function importFileToTipTap(
  fileName: string,
  buffer: ArrayBuffer
): Promise<RichTextImportResult> {
  const ext = fileName.split(".").pop()?.toLowerCase() ?? ""
  if (ext === "docx" || ext === "docm") {
    return importDocxToTipTap(buffer)
  }
  const text = new TextDecoder().decode(buffer)
  if (ext === "html" || ext === "htm") {
    return { content: htmlToTipTapDoc(text) }
  }
  const lines = text.split(/\r?\n/)
  return {
    content: {
      type: "doc",
      content: lines.map((line) => ({
        type: "paragraph",
        content: line ? [{ type: "text", text: line }] : [],
      })),
    },
  }
}
