﻿"use client"

import { useEffect, useRef } from "react"
import dynamic from "next/dynamic"
import { MEDIA_FILE_BASE } from "@/lib/media-manager/file-names"
import { stageImage } from "@/lib/media-manager/stage"
import type { StagedImage } from "@/lib/media-manager/types"

const Editor = dynamic(
  () => import("@tinymce/tinymce-react").then((mod) => mod.Editor),
  { ssr: false }
)

const MAX_IMAGE_SIZE_BYTES = 1 * 1024 * 1024

interface RichTextEditorProps {
  value: string
  onChange: (content: string) => void
  getArticleSlug?: () => string
  onStageContentImage?: (staged: StagedImage) => void
}

export function RichTextEditor({
  value,
  onChange,
  getArticleSlug,
  onStageContentImage,
}: RichTextEditorProps) {
  const tinyKey = process.env.NEXT_PUBLIC_TINYMCE_API_KEY?.trim() ?? ""
  const tinyScriptSrc = tinyKey ? `https://cdn.tiny.cloud/1/${tinyKey}/tinymce/8/tinymce.min.js` : undefined
  const latestSlugRef = useRef(getArticleSlug)

  useEffect(() => {
    latestSlugRef.current = getArticleSlug
  }, [getArticleSlug])

  function isBlobLike(value: unknown): value is Blob {
    return (
      typeof value === "object" &&
      value !== null &&
      typeof (value as Blob).size === "number" &&
      typeof (value as Blob).slice === "function"
    )
  }

  function mimeFromBlobInfo(blobInfo: { blob?: () => Blob; name?: () => string }): string {
    try {
      const b = typeof blobInfo?.blob === "function" ? blobInfo.blob() : undefined
      if (isBlobLike(b) && typeof b.type === "string" && b.type.startsWith("image/")) {
        return b.type
      }
    } catch {
      /* ignore */
    }
    const name = typeof blobInfo?.name === "function" ? String(blobInfo.name()) : ""
    const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : ""
    const map: Record<string, string> = {
      png: "image/png",
      jpg: "image/jpeg",
      jpeg: "image/jpeg",
      gif: "image/gif",
      webp: "image/webp",
      svg: "image/svg+xml",
      bmp: "image/bmp",
      avif: "image/avif",
    }
    return map[ext] ?? "image/png"
  }

  function assertSlugForImage(): string {
    const slug = latestSlugRef.current?.().trim() ?? ""
    if (!slug) {
      throw new Error("Please enter the article slug before adding content images.")
    }
    return slug
  }

  function stageBlob(file: Blob, fileName?: string): StagedImage {
    assertSlugForImage()
    const type = file.type?.startsWith("image/") ? file.type : mimeFromBlobInfo({ name: () => fileName ?? "" })
    const stagedFile =
      file instanceof File
        ? file
        : new File([file], fileName || "content-image.png", { type })
    const staged = stageImage(stagedFile, {
      maxBytes: MAX_IMAGE_SIZE_BYTES,
      fileBaseName: MEDIA_FILE_BASE.contentImages,
    })
    onStageContentImage?.(staged)
    return staged
  }

  async function uploadFromTinyMceBlobInfo(blobInfo: {
    blob?: () => Blob
    base64?: () => string
    name?: () => string
  }): Promise<string> {
    const blobRaw = typeof blobInfo?.blob === "function" ? blobInfo.blob() : undefined
    if (isBlobLike(blobRaw)) {
      const staged = stageBlob(blobRaw, typeof blobInfo?.name === "function" ? blobInfo.name() : undefined)
      return staged.previewUrl
    }

    const b64 = typeof blobInfo?.base64 === "function" ? blobInfo.base64() : ""
    if (typeof b64 === "string" && b64.length > 0) {
      const mime = mimeFromBlobInfo(blobInfo)
      const binary = atob(b64)
      const bytes = new Uint8Array(binary.length)
      for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i)
      const staged = stageBlob(new Blob([bytes], { type: mime }), "content-image.png")
      return staged.previewUrl
    }

    throw new Error("Invalid image data")
  }

  return (
    <div className="flex flex-col gap-1.5">
      <label className="text-xs font-bold text-foreground font-sans uppercase tracking-wider">
        Article Content
      </label>

      <div className="rounded-xl overflow-hidden border border-border">
        <Editor
          apiKey={tinyKey || undefined}
          tinymceScriptSrc={tinyScriptSrc}
          value={value}
          onEditorChange={(content) => onChange(content)}
          init={{
            height: 500,
            menubar: true,
            plugins: "link image table lists advlist",
            toolbar:
              "undo redo | formatselect | bold italic underline forecolor backcolor | alignleft aligncenter alignright | bullist numlist | link image table tableprops tablerowprops tablecellprops tablemergecells tablesplitcells | removeformat",
            automatic_uploads: true,
            paste_data_images: true,
            file_picker_types: "image",
            image_uploadtab: false,
            file_picker_callback: (callback, _value, meta) => {
              if (meta.filetype === "image") {
                const input = document.createElement("input")
                input.setAttribute("type", "file")
                input.setAttribute("accept", "image/*")
                input.onchange = () => {
                  const file = input.files?.[0]
                  if (!file) return
                  try {
                    const staged = stageBlob(file, file.name)
                    callback(staged.previewUrl, { alt: file.name })
                  } catch (err) {
                    console.error("file picker stage failed", err)
                  }
                }
                input.click()
              }
            },
            images_upload_handler: async (blobInfo) => uploadFromTinyMceBlobInfo(blobInfo),
            color_map: [
              "000000", "Black",
              "FF0000", "Red",
              "00AEEF", "Blue",
              "2BB673", "Green",
              "FFFF00", "Yellow",
              "FFFFFF", "White",
            ],
            verify_html: false,
            paste_remove_styles_if_webkit: false,
            paste_webkit_styles: "all",
            content_style:
              "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 14px; color: #1a1a2e; background: #fff; padding: 16px; }",
            branding: false,
            resize: false,
            statusbar: false,
            skin: "oxide",
          }}
        />
      </div>

      <p className="text-xs text-muted-foreground font-sans">
        Format your article in the editor. Images are stored locally until you save draft, save a section, or publish.
      </p>
    </div>
  )
}
