import { commitImage } from "@/lib/media-manager/commit"
import { MEDIA_FILE_BASE } from "@/lib/media-manager/file-names"
import { revokeStagedImage } from "@/lib/media-manager/stage"
import type { StagedImage } from "@/lib/media-manager/types"
import type { Language } from "@/components/dashboard/shared/LanguageTabs"
import { nextContentImageFileIndex } from "@/lib/media-manager/article-content-index"

export interface ArticlePendingImages {
  coverImage: StagedImage | null
  contentImages: StagedImage[]
  gallery: Map<number, StagedImage>
  seoImages: Map<number, StagedImage>
}

export function createEmptyArticlePendingImages(): ArticlePendingImages {
  return {
    coverImage: null,
    contentImages: [],
    gallery: new Map(),
    seoImages: new Map(),
  }
}

export function articlePendingHasAny(pending: ArticlePendingImages): boolean {
  return (
    !!pending.coverImage ||
    pending.contentImages.length > 0 ||
    pending.gallery.size > 0 ||
    pending.seoImages.size > 0
  )
}

export function clearArticlePendingImages(pending: ArticlePendingImages): void {
  if (pending.coverImage) revokeStagedImage(pending.coverImage)
  for (const staged of pending.contentImages) revokeStagedImage(staged)
  for (const staged of pending.gallery.values()) revokeStagedImage(staged)
  for (const staged of pending.seoImages.values()) revokeStagedImage(staged)
  pending.coverImage = null
  pending.contentImages = []
  pending.gallery.clear()
  pending.seoImages.clear()
}

export interface ArticleLocaleContentSlice {
  content: string
}

export interface ArticleFormImageSlice {
  image: string
  imagePublicId: string
  content: string
  translations: Record<Language, ArticleLocaleContentSlice>
}

export interface CommitPendingArticleImagesOptions {
  /** Current form slug — sole source of truth for R2 storage paths. */
  articleSlug: string
  articleId?: string
  pending: ArticlePendingImages
}

function replaceBlobUrlsInHtml(
  html: string,
  replacements: Map<string, { url: string; publicId: string }>
): string {
  let out = html
  for (const [blobUrl, committed] of replacements) {
    if (!blobUrl || !committed.url) continue
    out = out.split(blobUrl).join(committed.url)
  }
  return out
}

/** Upload pending article images, merge URLs into form slice, clear pending. */
export async function commitPendingArticleImages(
  slice: ArticleFormImageSlice,
  options: CommitPendingArticleImagesOptions
): Promise<ArticleFormImageSlice> {
  const articleSlug = options.articleSlug.trim()
  if (!articleSlug) {
    throw new Error("Article slug is required before saving images.")
  }

  let image = slice.image
  let imagePublicId = slice.imagePublicId
  let content = slice.content
  const translations = {
    en: { content: slice.translations.en.content },
    ar: { content: slice.translations.ar.content },
    tr: { content: slice.translations.tr.content },
  }

  if (options.pending.coverImage) {
    const staged = options.pending.coverImage
    const committed = await commitImage({
      entity: "article",
      staged,
      articleSlug,
      section: "cover-image",
      fileBaseName: MEDIA_FILE_BASE.coverImage,
      articleId: options.articleId,
    })
    revokeStagedImage(staged)
    image = committed.url
    imagePublicId = committed.publicId
    options.pending.coverImage = null
  }

  if (options.pending.contentImages.length > 0) {
    const htmlContents = [content, translations.ar.content, translations.tr.content, translations.en.content]
    let fileIndex = nextContentImageFileIndex(articleSlug, htmlContents)
    const blobReplacements = new Map<string, { url: string; publicId: string }>()

    for (const staged of options.pending.contentImages) {
      const committed = await commitImage({
        entity: "article",
        staged,
        articleSlug,
        section: "content-images",
        fileBaseName: MEDIA_FILE_BASE.contentImages,
        fileIndex,
        articleId: options.articleId,
      })
      fileIndex += 1
      blobReplacements.set(staged.previewUrl, {
        url: committed.url,
        publicId: committed.publicId,
      })
      revokeStagedImage(staged)
    }
    options.pending.contentImages = []

    content = replaceBlobUrlsInHtml(content, blobReplacements)
    translations.en.content = replaceBlobUrlsInHtml(translations.en.content, blobReplacements)
    translations.ar.content = replaceBlobUrlsInHtml(translations.ar.content, blobReplacements)
    translations.tr.content = replaceBlobUrlsInHtml(translations.tr.content, blobReplacements)
  }

  for (const [index, staged] of [...options.pending.gallery.entries()].sort(([a], [b]) => a - b)) {
    const committed = await commitImage({
      entity: "article",
      staged,
      articleSlug,
      section: "gallery",
      fileBaseName: MEDIA_FILE_BASE.gallery,
      fileIndex: index,
      articleId: options.articleId,
    })
    revokeStagedImage(staged)
    options.pending.gallery.delete(index)
    void committed
  }

  for (const [index, staged] of [...options.pending.seoImages.entries()].sort(([a], [b]) => a - b)) {
    const committed = await commitImage({
      entity: "article",
      staged,
      articleSlug,
      section: "seo-images",
      fileBaseName: MEDIA_FILE_BASE.seoImages,
      fileIndex: index,
      articleId: options.articleId,
    })
    revokeStagedImage(staged)
    options.pending.seoImages.delete(index)
    void committed
  }

  return {
    image,
    imagePublicId,
    content,
    translations,
  }
}
