import {
  canonicalPublicMediaUrl,
  extractMediaKeyFromUrl,
  extractMediaKeysFromHtml,
  rewriteLegacyMediaPublicUrl,
} from "@/lib/r2-media-url"
import {
  getPersistedImageUrlError,
  isExternalImageUrl,
} from "@/lib/media-manager/local-upload-policy"

function isImageValueFieldKey(key: string): boolean {
  const lower = key.toLowerCase()
  if (lower.includes("publicid")) return false
  if (lower.includes("heading") || lower.includes("folder")) return false
  if (lower.includes("videourl") || lower.includes("virtualtour")) return false
  if (lower.includes("mapurl") || lower.includes("sociallink")) return false
  if (lower === "image" || lower === "logo" || lower === "avatar") return true
  if (lower === "mainimage" || lower === "beforeimage" || lower === "afterimage") return true
  if (lower.endsWith("image") && !lower.endsWith("images")) return true
  return false
}

function isHtmlContentFieldKey(key: string): boolean {
  const lower = key.toLowerCase()
  return lower === "content" || lower === "overview" || lower.endsWith("content")
}

function scanHtmlForExternalImages(html: string, fieldPath: string): string | null {
  const imgSrcRegex = /<img\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi
  let match: RegExpExecArray | null = null
  while ((match = imgSrcRegex.exec(html)) !== null) {
    const src = match[1]
    if (isExternalImageUrl(src)) {
      return `External image links in HTML are not allowed (${fieldPath}). Upload images from your device.`
    }
    const err = getPersistedImageUrlError(src, { fieldPath: `${fieldPath} <img src>`, mode: "persist" })
    if (err) return err
  }
  return null
}

function walkPayload(value: unknown, path: string, errors: string[]): void {
  if (value === null || value === undefined) return

  if (typeof value === "string") {
    const key = path.split(".").pop() ?? path
    if (isHtmlContentFieldKey(key)) {
      const htmlErr = scanHtmlForExternalImages(value, path)
      if (htmlErr) errors.push(htmlErr)
      return
    }
    if (isImageValueFieldKey(key)) {
      const err = getPersistedImageUrlError(value, { fieldPath: path, mode: "persist" })
      if (err) errors.push(err)
    }
    return
  }

  if (Array.isArray(value)) {
    value.forEach((item, index) => walkPayload(item, `${path}[${index}]`, errors))
    return
  }

  if (typeof value === "object") {
    for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
      const nextPath = path ? `${path}.${key}` : key
      walkPayload(nested, nextPath, errors)
    }
  }
}

function normalizePersistedImageString(value: string): string {
  const raw = value.trim()
  if (!raw || raw.startsWith("blob:") || /^data:image\//i.test(raw)) return raw
  const mediaKey = extractMediaKeyFromUrl(raw)
  if (mediaKey) return canonicalPublicMediaUrl(raw, mediaKey)
  return rewriteLegacyMediaPublicUrl(raw)
}

function walkNormalizePersistedImages(value: unknown, path: string): unknown {
  if (value === null || value === undefined) return value

  if (typeof value === "string") {
    const key = path.split(".").pop() ?? path
    if (isImageValueFieldKey(key)) return normalizePersistedImageString(value)
    if (isHtmlContentFieldKey(key)) {
      return value.replace(
        /(<img\b[^>]*\bsrc\s*=\s*["'])([^"']+)(["'][^>]*>)/gi,
        (_match, prefix: string, src: string, suffix: string) => {
          const trimmed = src.trim()
          if (!trimmed || trimmed.startsWith("blob:") || /^data:image\//i.test(trimmed)) {
            return `${prefix}${src}${suffix}`
          }
          const mediaKey = extractMediaKeyFromUrl(trimmed)
          const next = mediaKey ? canonicalPublicMediaUrl(trimmed, mediaKey) : rewriteLegacyMediaPublicUrl(trimmed)
          return `${prefix}${next}${suffix}`
        }
      )
    }
    return value
  }

  if (Array.isArray(value)) {
    return value.map((item, index) => walkNormalizePersistedImages(item, `${path}[${index}]`))
  }

  if (typeof value === "object") {
    const out: Record<string, unknown> = {}
    for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
      const nextPath = path ? `${path}.${key}` : key
      out[key] = walkNormalizePersistedImages(nested, nextPath)
    }
    return out
  }

  return value
}

/** Rewrite stored image fields to canonical managed URLs before validation/save. */
export function normalizePersistedMediaInPayload<T>(payload: T): T {
  return walkNormalizePersistedImages(payload, "") as T
}

function walkFindBlobImageField(value: unknown, path: string): string | null {
  if (value === null || value === undefined) return null

  if (typeof value === "string") {
    const key = path.split(".").pop() ?? path
    if (isImageValueFieldKey(key) && value.trim().startsWith("blob:")) return path
    return null
  }

  if (Array.isArray(value)) {
    for (let index = 0; index < value.length; index += 1) {
      const found = walkFindBlobImageField(value[index], `${path}[${index}]`)
      if (found) return found
    }
    return null
  }

  if (typeof value === "object") {
    for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {
      const nextPath = path ? `${path}.${key}` : key
      const found = walkFindBlobImageField(nested, nextPath)
      if (found) return found
    }
  }

  return null
}

/** First `image` / `avatar` / `logo` field still holding a blob preview URL. */
export function findFirstBlobImageFieldPath(payload: unknown): string | null {
  return walkFindBlobImageField(payload, "")
}

/** Scan API save payload for external / pasted image URLs. Returns first error or null. */
export function getPersistedMediaValidationError(payload: unknown): string | null {
  const errors: string[] = []
  walkPayload(payload, "", errors)
  return errors[0] ?? null
}

/** Collect external img src values from HTML (articles, etc.). */
export function findExternalImagesInHtml(html?: string | null): string[] {
  const raw = typeof html === "string" ? html : ""
  if (!raw.trim()) return []
  const found: string[] = []
  const imgSrcRegex = /<img\b[^>]*\bsrc\s*=\s*["']([^"']+)["'][^>]*>/gi
  let match: RegExpExecArray | null = null
  while ((match = imgSrcRegex.exec(raw)) !== null) {
    if (isExternalImageUrl(match[1])) found.push(match[1])
  }
  return found
}

/** Ensure inline article images in HTML use managed URLs only. */
export function assertManagedHtmlImages(html: string, fieldPath = "content"): void {
  const external = findExternalImagesInHtml(html)
  if (external.length > 0) {
    throw new Error(
      `External image links in ${fieldPath} are not allowed. Upload images from your device.`
    )
  }
}

export { extractMediaKeysFromHtml }
