import type { TipTapJSON } from "@/lib/drive/richtext-import"
import {
  DOCS_GRAPHIC_DEFAULTS,
  imageAttrsToGraphic,
  parseGraphicAttrs,
  type DocsGraphicWrap,
  type DocsGraphicPlacement,
} from "./docs-graphic-types.ts"

const LEGACY_IMAGE_KEYS = [
  "src",
  "alt",
  "title",
  "width",
  "height",
  "placement",
  "wrap",
  "floatSide",
  "x",
  "y",
  "rotationDeg",
  "zIndex",
  "cropX",
  "cropY",
  "cropWidth",
  "cropHeight",
  "cropShape",
  "lockAspectRatio",
  "imageFit",
  "imageFitAnchorH",
  "imageFitAnchorV",
  "assetId",
  "opacity",
  "shadow",
] as const

const DOCX_WRAP_MAP: Record<string, DocsGraphicWrap> = {
  inline: "inline",
  square: "square",
  tight: "tight",
  through: "through",
  topAndBottom: "top-bottom",
  topbottom: "top-bottom",
  behind: "behind",
  infront: "in-front",
  inFront: "in-front",
}

const DOCX_PLACEMENT_MAP: Record<string, DocsGraphicPlacement> = {
  inline: "inline",
  block: "block",
  absolute: "absolute",
  anchored: "absolute",
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value)
}

function paragraphIsSingleImage(node: TipTapJSON): boolean {
  if (node.type !== "paragraph" || !Array.isArray(node.content) || node.content.length !== 1) {
    return false
  }
  const child = node.content[0] as TipTapJSON
  return child.type === "image" || child.type === "docsInlineGraphic"
}

function upgradeImageNode(raw: TipTapJSON, asBlock: boolean): TipTapJSON {
  const attrs = isRecord(raw.attrs) ? raw.attrs : {}
  const graphic = imageAttrsToGraphic(attrs)
  if (asBlock) {
    return {
      type: "docsGraphic",
      attrs: {
        ...graphic,
        placement: graphic.placement === "inline" ? "block" : graphic.placement,
        wrap: graphic.wrap === "inline" ? "square" : graphic.wrap,
      },
    }
  }
  return {
    type: "docsInlineGraphic",
    attrs: {
      ...graphic,
      placement: "inline",
      wrap: graphic.wrap === "square" ? "inline" : graphic.wrap,
    },
  }
}

function upgradeGraphicNode(raw: TipTapJSON): TipTapJSON {
  const attrs = isRecord(raw.attrs) ? parseGraphicAttrs(raw.attrs) : DOCS_GRAPHIC_DEFAULTS
  if (raw.type === "docsGraphic") {
    return { type: "docsGraphic", attrs }
  }
  if (raw.type === "docsInlineGraphic") {
    return { type: "docsInlineGraphic", attrs }
  }
  if (raw.type === "image") {
    return upgradeImageNode(raw, false)
  }
  return raw
}

function mapImportedGraphicAttrs(attrs: Record<string, unknown>): Record<string, unknown> {
  const wrapRaw = attrs.wrap ?? attrs.textWrap ?? attrs.layout
  const placementRaw = attrs.placement ?? attrs.position ?? attrs.layoutMode
  const wrap =
    typeof wrapRaw === "string" ? (DOCX_WRAP_MAP[wrapRaw] ?? wrapRaw) : undefined
  const placement =
    typeof placementRaw === "string"
      ? (DOCX_PLACEMENT_MAP[placementRaw] ?? placementRaw)
      : undefined

  return {
    ...attrs,
    ...(wrap ? { wrap } : {}),
    ...(placement ? { placement } : {}),
    floatSide: attrs.floatSide ?? attrs.align ?? attrs.horizontalAlign,
  }
}

/** Upgrade legacy `image` nodes and normalize graphic attrs after DOCX import. */
export function normalizeImportedGraphics(content: TipTapJSON): TipTapJSON {
  const walk = (node: unknown): unknown => {
    if (!node || typeof node !== "object") return node
    if (Array.isArray(node)) return node.map(walk)

    const record = node as TipTapJSON
    if (record.type === "image" && isRecord(record.attrs)) {
      const mapped = mapImportedGraphicAttrs(record.attrs)
      const placement = mapped.placement as string | undefined
      const wrap = mapped.wrap as string | undefined
      const asBlock =
        wrap === "square" ||
        wrap === "tight" ||
        wrap === "through" ||
        wrap === "top-bottom" ||
        wrap === "behind" ||
        wrap === "in-front" ||
        placement === "block" ||
        placement === "absolute"
      return upgradeImageNode({ ...record, attrs: mapped }, asBlock)
    }
    if (
      (record.type === "docsGraphic" || record.type === "docsInlineGraphic") &&
      isRecord(record.attrs)
    ) {
      record.attrs = parseGraphicAttrs(mapImportedGraphicAttrs(record.attrs))
    }

    if (record.type === "paragraph" && paragraphIsSingleImage(record)) {
      const child = (record.content as TipTapJSON[])[0]
      if (child.type === "image") {
        const upgraded = upgradeImageNode(
          { ...child, attrs: mapImportedGraphicAttrs((child.attrs as Record<string, unknown>) ?? {}) },
          true
        )
        return upgraded
      }
    }

    if (Array.isArray(record.content)) {
      const nextContent = record.content.map(walk).filter(Boolean)
      if (record.type === "paragraph") {
        return { ...record, content: nextContent }
      }
      return { ...record, content: nextContent }
    }

    if (record.type === "image" || record.type === "docsGraphic" || record.type === "docsInlineGraphic") {
      return upgradeGraphicNode(record)
    }

    return record
  }

  const normalized = walk(content)
  if (!normalized || typeof normalized !== "object") {
    return { type: "doc", content: [{ type: "paragraph" }] }
  }
  return normalized as TipTapJSON
}

/** Keep legacy inline images compatible with @tiptap/extension-image when needed. */
export function normalizeLegacyImageAttrs(content: TipTapJSON): TipTapJSON {
  const walk = (node: unknown): unknown => {
    if (!node || typeof node !== "object") return node
    if (Array.isArray(node)) return node.map(walk)
    const record = node as TipTapJSON
    if (record.type === "image" && isRecord(record.attrs)) {
      const attrs: Record<string, unknown> = {}
      for (const key of LEGACY_IMAGE_KEYS) {
        const value = record.attrs[key]
        if (value != null && value !== "") attrs[key] = value
      }
      if (typeof attrs.src !== "string" || !attrs.src) return null
      return { ...record, attrs }
    }
    if (Array.isArray(record.content)) {
      return { ...record, content: record.content.map(walk).filter(Boolean) }
    }
    return record
  }
  const normalized = walk(content)
  if (!normalized || typeof normalized !== "object") {
    return { type: "doc", content: [{ type: "paragraph" }] }
  }
  return normalized as TipTapJSON
}
