import { randomUUID } from "crypto"
import { NextRequest, NextResponse } from "next/server"
import type { Procedure_status } from "@prisma/client"
import { prisma } from "@/lib/prisma"
import {
  buildContentAudit,
  contentAuditProcedureInclude,
  contentAuditUpdateData,
  touchUpdatedAt,
  requireContentAuditActor,
} from "@/lib/content-audit"
import {
  applyProcedureCtaDefaults,
} from "@/lib/procedure-cta-contact-form-defaults"
import { applyProcedureDoctorsSectionDefaults } from "@/lib/procedure-doctors-section-defaults"
import { applyProcedureTranslationsFormDefaults } from "@/lib/procedure-form-locale-defaults"
import {
  collectProcedureImagePublicIds,
  emptyProcedureLocaleFormFields,
  folderFromMediaKey,
  normalizeProcedureBasicInfoJson,
  normalizeProcedureSharedImages,
  procedureMediaBaseForSlug,
  PROCEDURE_LOCALES,
  type ProcedureLocale,
} from "@/lib/procedure-db"
import { validateUrlSlug } from "@/lib/slug-validation"
import { getPersistedMediaValidationError } from "@/lib/media-manager/validate-persisted-payload"
import {
  normalizeSavePayloadMediaToCurrentBase,
  reconcileEntityMediaAfterSave,
} from "@/lib/media-manager/slug-media-reconcile"
import {
  deleteImage,
  deletePrefixIfEmpty,
  purgePrefixExcept,
} from "@/lib/r2-storage"
import { relocateMediaBases, rewriteMediaPathsMulti } from "@/lib/media-manager/slug-relocate"
import { normalizePersistedMediaUrls } from "@/lib/normalize-persisted-media-urls"

type RouteContext = { params: Promise<{ id: string }> }

export const dynamic = "force-dynamic"
export const revalidate = 0

function fail(message: string, status = 400) {
  return NextResponse.json({ success: false as const, error: message }, { status })
}

function isSchemaDriftError(err: unknown): boolean {
  if (err && typeof err === "object" && "code" in err && (err as { code?: string }).code === "P2022") {
    return true
  }
  const message = err instanceof Error ? err.message : ""
  return /unknown column|videosectionjson|createdbyid|updatedbyid/i.test(message)
}

function parseString(value: unknown): string {
  return typeof value === "string" ? value.trim() : ""
}

function parseNumber(value: unknown): number {
  const n = typeof value === "number" ? value : Number(value)
  return Number.isFinite(n) ? n : 0
}

function parseBasicInfoFields(value: unknown): Record<string, string> {
  const record = value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {}
  return {
    startingFromPrice: parseString(record.startingFromPrice),
    duration: parseString(record.duration),
    hospitalStay: parseString(record.hospitalStay),
    recovery: parseString(record.recovery),
  }
}

function parseJsonOr<T>(value: string | null | undefined, fallback: T): T {
  if (!value) return fallback
  try {
    return JSON.parse(value) as T
  } catch {
    return fallback
  }
}

function asJsonRecord(value: unknown): Record<string, unknown> {
  return value && typeof value === "object" && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : {}
}

function parseJsonRecordOr(value: string | null | undefined, fallback: unknown = {}): Record<string, unknown> {
  return {
    ...asJsonRecord(fallback),
    ...asJsonRecord(parseJsonOr(value, {})),
  }
}

function toJson(value: unknown): string {
  return JSON.stringify(value ?? {})
}

function hasMeaningfulContent(value: unknown): boolean {
  if (value === null || value === undefined) return false
  if (typeof value === "string") return value.trim() !== ""
  if (typeof value === "number" || typeof value === "boolean") return true
  if (Array.isArray(value)) return value.some((item) => hasMeaningfulContent(item))
  if (typeof value === "object") {
    return Object.values(value as Record<string, unknown>).some((item) => hasMeaningfulContent(item))
  }
  return false
}

function stripSharedImageLikeFields(value: unknown): unknown {
  if (value === null || value === undefined) return value
  if (Array.isArray(value)) return value.map((item) => stripSharedImageLikeFields(item))
  if (typeof value !== "object") return value

  const record = value as Record<string, unknown>
  const out: Record<string, unknown> = {}
  for (const [key, raw] of Object.entries(record)) {
    const lower = key.toLowerCase()
    if (lower.includes("image") || lower.includes("avatar") || lower.includes("publicid")) {
      continue
    }
    out[key] = stripSharedImageLikeFields(raw)
  }
  return out
}

function hasMeaningfulLocaleTranslation(localeData: ReturnType<typeof getLocaleData>): boolean {
  return hasMeaningfulContent({
    name: localeData.name,
    description: localeData.description,
    stayCity: localeData.stayCity,
    basicInfo: stripSharedImageLikeFields(localeData.basicInfo),
    overview: stripSharedImageLikeFields(localeData.overview),
    countriesComparison: stripSharedImageLikeFields(localeData.countriesComparison),
    process: stripSharedImageLikeFields(localeData.process),
    benefits: stripSharedImageLikeFields(localeData.benefits),
    beforeAfterSection: stripSharedImageLikeFields(localeData.beforeAfterSection),
    doctorsSection: stripSharedImageLikeFields(localeData.doctorsSection),
    recovery: stripSharedImageLikeFields(localeData.recovery),
    packageSection: stripSharedImageLikeFields(localeData.packageSection),
    reviewsSection: stripSharedImageLikeFields(localeData.reviewsSection),
    faqSection: stripSharedImageLikeFields(localeData.faqSection),
    related: stripSharedImageLikeFields(localeData.related),
    cta: stripSharedImageLikeFields(localeData.cta),
    videoSection: stripSharedImageLikeFields(localeData.videoSection),
  })
}

function getLocaleData(translations: Record<string, unknown>, locale: string) {
  const raw =
    translations[locale] && typeof translations[locale] === "object"
      ? (translations[locale] as Record<string, unknown>)
      : {}
  return {
    name: parseString(raw.name),
    description: parseString(raw.description),
    stayCity: parseString(raw.stayCity),
    basicInfo: parseBasicInfoFields(raw.basicInfo ?? {}),
    overview: raw.overview ?? {},
    countriesComparison: raw.countriesComparison ?? {},
    process: raw.process ?? {},
    benefits: raw.benefits ?? {},
    beforeAfterSection: raw.beforeAfterSection ?? {},
    doctorsSection: {
      doctorsSectionEyebrow: parseString(raw.doctorsSectionEyebrow),
      doctorsSectionTitle: parseString(raw.doctorsSectionTitle),
    },
    recovery: raw.recovery ?? {},
    packageSection: raw.packageSection ?? {},
    reviewsSection: {
      reviews: Array.isArray(raw.reviews) ? raw.reviews : [],
      reviewsEyebrow: parseString(raw.reviewsEyebrow),
      reviewsTitle: parseString(raw.reviewsTitle),
    },
    faqSection: {
      faqItems: Array.isArray(raw.faqItems) ? raw.faqItems : [],
      faqEyebrow: parseString(raw.faqEyebrow),
      faqTitle: parseString(raw.faqTitle),
      faqSubtitle: parseString(raw.faqSubtitle),
      faqButtonText: parseString(raw.faqButtonText),
      faqButtonLink: parseString(raw.faqButtonLink),
    },
    related: raw.related ?? {},
    cta: raw.cta ?? {},
    videoSection: raw.videoSection ?? {},
  }
}

function buildFormFromDb(
  row: Record<string, unknown>,
  trRows: Array<Record<string, unknown>>
): Record<string, unknown> {
  const translations: Record<string, Record<string, unknown>> = {
    en: {},
    ar: {},
    tr: {},
  }
  const rowBasicInfoFromJson = normalizeProcedureBasicInfoJson(
    parseJsonOr<Record<string, unknown>>(row.basicInfoJson as string, {})
  )

  for (const locale of PROCEDURE_LOCALES) {
    const tr = trRows.find((x) => x.locale === locale) ?? null
    if (!tr && locale !== "en") {
      const emptyFields = emptyProcedureLocaleFormFields()
      translations[locale] = applyProcedureDoctorsSectionDefaults(
        {
          ...emptyFields,
          cta: applyProcedureCtaDefaults(emptyFields.cta, locale as ProcedureLocale),
        },
        locale as ProcedureLocale
      )
      continue
    }
    const localeDefaults = emptyProcedureLocaleFormFields()
    const source = tr ?? row
    const doctorsSection = parseJsonRecordOr(source.doctorsSectionJson as string)
    const reviewsSection = parseJsonRecordOr(source.reviewsSectionJson as string, {
      reviews: [],
      reviewsEyebrow: "",
      reviewsTitle: "",
    })
    const faqSection = parseJsonRecordOr(source.faqSectionJson as string, {
      faqItems: [],
      faqEyebrow: "",
      faqTitle: "",
      faqSubtitle: "",
      faqButtonText: "",
      faqButtonLink: "",
    })
    const sourceTyped = source as { stayCity?: string | null; basicInfoJson?: string | null }
    const localeBasicInfoJson =
      locale === "en"
        ? rowBasicInfoFromJson
        : parseJsonRecordOr(sourceTyped.basicInfoJson as string, {})
    const localeBasicInfo = parseBasicInfoFields(localeBasicInfoJson)
    translations[locale] = applyProcedureDoctorsSectionDefaults(
      {
        name:
          parseString(source.name) || (locale === "en" ? parseString(row.name) : ""),
        description:
          parseString(source.description) || (locale === "en" ? parseString(row.description) : ""),
        overview: parseJsonRecordOr(source.overviewJson as string, localeDefaults.overview),
        countriesComparison: parseJsonRecordOr(source.countriesComparisonJson as string, localeDefaults.countriesComparison),
        process: parseJsonRecordOr(source.processJson as string, localeDefaults.process),
        benefits: parseJsonRecordOr(source.benefitsJson as string, localeDefaults.benefits),
        beforeAfterSection: parseJsonRecordOr(source.beforeAfterSectionJson as string, localeDefaults.beforeAfterSection),
        doctorsSectionEyebrow: parseString(doctorsSection.doctorsSectionEyebrow),
        doctorsSectionTitle: parseString(doctorsSection.doctorsSectionTitle),
        stayCity:
          parseString(sourceTyped.stayCity) ||
          parseString(doctorsSection.stayCity) ||
          (locale === "en" ? parseString(rowBasicInfoFromJson.stayCity) : ""),
        recovery: parseJsonRecordOr(source.recoveryJson as string, localeDefaults.recovery),
        packageSection: parseJsonRecordOr(source.packageSectionJson as string, localeDefaults.packageSection),
        reviews: Array.isArray(reviewsSection.reviews) ? reviewsSection.reviews : [],
        reviewsEyebrow: parseString(reviewsSection.reviewsEyebrow),
        reviewsTitle: parseString(reviewsSection.reviewsTitle),
        faqItems: Array.isArray(faqSection.faqItems) ? faqSection.faqItems : [],
        faqEyebrow: parseString(faqSection.faqEyebrow),
        faqTitle: parseString(faqSection.faqTitle),
        faqSubtitle: parseString(faqSection.faqSubtitle),
        faqButtonText: parseString(faqSection.faqButtonText),
        faqButtonLink: parseString(faqSection.faqButtonLink),
        related: parseJsonRecordOr(source.relatedJson as string, localeDefaults.related),
        cta: applyProcedureCtaDefaults(
          parseJsonRecordOr(source.ctaJson as string, localeDefaults.cta),
          locale as ProcedureLocale
        ),
        videoSection: parseJsonRecordOr(source.videoSectionJson as string, localeDefaults.videoSection),
        basicInfo: localeBasicInfo,
      },
      locale as ProcedureLocale
    )
  }

  return normalizePersistedMediaUrls({
    categoryId: parseString(row.categoryId),
    isFeatured: Boolean(row.isFeatured),
    basicInfo: {
      ...rowBasicInfoFromJson,
      slug: parseString(row.slug) || parseString(rowBasicInfoFromJson.slug),
      mainImage: parseString(row.image) || parseString(rowBasicInfoFromJson.mainImage),
      mainImagePublicId:
        parseString(row.imagePublicId) || parseString(rowBasicInfoFromJson.mainImagePublicId),
    },
    quickStats: parseJsonOr(row.quickStatsJson as string, []),
    translations,
  })
}

function buildProcedureImageSnapshot(row: Record<string, unknown>, trRows: Array<Record<string, unknown>>) {
  const form = buildFormFromDb(row, trRows)
  const ids = collectProcedureImagePublicIds({
    image: row.image,
    imagePublicId: row.imagePublicId,
    basicInfo: form.basicInfo,
    translations: form.translations,
    quickStats: form.quickStats,
  })
  const folders = new Set<string>()
  for (const id of ids) {
    const folder = folderFromMediaKey(id)
    if (folder) folders.add(folder)
  }
  return { ids, folders }
}

export async function GET(_req: NextRequest, context: RouteContext) {
  try {
    const { id } = await context.params
    if (!id) return fail("Missing id")
    let row: Record<string, unknown> | null = null
    let trRows: Array<Record<string, unknown>> = []

    try {
      const richRow = await prisma.procedure.findUnique({
        where: { id },
        include: {
          Category: { select: { id: true, name: true, color: true } },
          ...contentAuditProcedureInclude,
        },
      })
      if (!richRow) return fail("Procedure not found", 404)
      row = richRow as unknown as Record<string, unknown>

      const richTrRows = await prisma.procedureTranslation.findMany({
        where: { procedureId: id },
        select: {
          locale: true,
          name: true,
          description: true,
          stayCity: true,
          basicInfoJson: true,
          overviewJson: true,
          countriesComparisonJson: true,
          processJson: true,
          benefitsJson: true,
          beforeAfterSectionJson: true,
          doctorsSectionJson: true,
          recoveryJson: true,
          packageSectionJson: true,
          reviewsSectionJson: true,
          faqSectionJson: true,
          relatedJson: true,
          ctaJson: true,
          videoSectionJson: true,
        } as any,
      })
      trRows = richTrRows as unknown as Array<Record<string, unknown>>
    } catch (err) {
      if (!isSchemaDriftError(err)) {
        console.warn("[procedures] GET [id] primary query failed, trying compatibility fallback:", err)
      }

      const compatRow = await prisma.procedure.findUnique({
        where: { id },
        include: {
          Category: { select: { id: true, name: true, color: true } },
        },
      })
      if (!compatRow) return fail("Procedure not found", 404)
      row = compatRow as unknown as Record<string, unknown>

      const compatTrRows = await prisma.procedureTranslation.findMany({
        where: { procedureId: id },
        select: {
          locale: true,
          name: true,
          description: true,
          stayCity: true,
          basicInfoJson: true,
          overviewJson: true,
          countriesComparisonJson: true,
          processJson: true,
          benefitsJson: true,
          beforeAfterSectionJson: true,
          doctorsSectionJson: true,
          recoveryJson: true,
          packageSectionJson: true,
          reviewsSectionJson: true,
          faqSectionJson: true,
          relatedJson: true,
          ctaJson: true,
        },
      })
      trRows = compatTrRows as unknown as Array<Record<string, unknown>>
    }

    const form = buildFormFromDb(row as unknown as Record<string, unknown>, trRows as unknown as Array<Record<string, unknown>>)
    const { Category, ...procedureBase } = row
    return NextResponse.json({
      success: true as const,
      procedure: {
        ...procedureBase,
        category: Category ?? null,
        form,
        audit: buildContentAudit(row as any),
      },
    })
  } catch (err) {
    console.error("[procedures] GET [id] error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to load procedure" }, { status: 500 })
  }
}

export async function PUT(req: NextRequest, context: RouteContext) {
  try {
    const auth = await requireContentAuditActor()
    if (!auth.ok) return auth.response

    const { id } = await context.params
    if (!id) return fail("Missing id")
    const existing = await prisma.procedure.findUnique({
      where: { id },
      select: {
        id: true,
        categoryId: true,
        slug: true,
        name: true,
        description: true,
        imagePublicId: true,
        isFeatured: true,
        rating: true,
        reviewCount: true,
        status: true,
        basicInfoJson: true,
        quickStatsJson: true,
        overviewJson: true,
        countriesComparisonJson: true,
        processJson: true,
        benefitsJson: true,
        beforeAfterSectionJson: true,
        doctorsSectionJson: true,
        recoveryJson: true,
        packageSectionJson: true,
        reviewsSectionJson: true,
        faqSectionJson: true,
        relatedJson: true,
        ctaJson: true,
        videoSectionJson: true,
      } as any,
    })
    if (!existing) return fail("Procedure not found", 404)

    const existingTranslations = await prisma.procedureTranslation.findMany({
      where: { procedureId: id },
      select: {
        locale: true,
        name: true,
        description: true,
        stayCity: true,
        basicInfoJson: true,
        overviewJson: true,
        countriesComparisonJson: true,
        processJson: true,
        benefitsJson: true,
        beforeAfterSectionJson: true,
        doctorsSectionJson: true,
        recoveryJson: true,
        packageSectionJson: true,
        reviewsSectionJson: true,
        faqSectionJson: true,
        relatedJson: true,
        ctaJson: true,
        videoSectionJson: true,
      } as any,
    })
    const existingRecord = existing as any
    const previousSnapshot = buildProcedureImageSnapshot(
      existing as unknown as Record<string, unknown>,
      existingTranslations as unknown as Array<Record<string, unknown>>
    )

    const payload = (await req.json()) as Record<string, unknown>
    const mediaError = getPersistedMediaValidationError(payload)
    if (mediaError) return fail(mediaError)
    const status: Procedure_status = payload.status === "draft" ? "draft" : "published"
    const isDraft = status === "draft"
    const categoryId = parseString(payload.categoryId) || existingRecord.categoryId
    let basicInfo =
      payload.basicInfo && typeof payload.basicInfo === "object"
        ? (payload.basicInfo as Record<string, unknown>)
        : {}
    let translationsInput =
      payload.translations && typeof payload.translations === "object"
        ? (payload.translations as Record<string, unknown>)
        : {}

    const slug = parseString(basicInfo.slug) || String(existingRecord.slug ?? "")
    const slugValidation = validateUrlSlug(slug)
    if (!slugValidation.valid) return fail(slugValidation.error ?? "Invalid slug")

    if (!isDraft) {
      if (!slug) return fail("Slug is required")
    }

    if (categoryId) {
      const category = await prisma.category.findUnique({ where: { id: categoryId }, select: { id: true } })
      if (!category) return fail("Selected category does not exist")
    }

    if (slug !== String(existingRecord.slug ?? "")) {
      const conflicting = await prisma.procedure.findFirst({ where: { slug, NOT: { id } }, select: { id: true } })
      if (conflicting) return fail("Slug already in use", 409)
    }

    const slugChanged = slug !== String(existingRecord.slug ?? "")
    const oldMediaBase = procedureMediaBaseForSlug(String(existingRecord.slug ?? ""))
    const newMediaBase = procedureMediaBaseForSlug(slug)
    if (slugChanged) {
      try {
        await relocateMediaBases([oldMediaBase], newMediaBase)
      } catch (relocateErr) {
        console.error("[procedures] slug media relocate failed:", relocateErr)
        return fail("Failed to move procedure images for the new slug", 500)
      }
      basicInfo = rewriteMediaPathsMulti(basicInfo, [oldMediaBase], newMediaBase) as Record<string, unknown>
      translationsInput = rewriteMediaPathsMulti(
        translationsInput,
        [oldMediaBase],
        newMediaBase
      ) as Record<string, unknown>
      if (Array.isArray(payload.quickStats)) {
        payload.quickStats = rewriteMediaPathsMulti(payload.quickStats, [oldMediaBase], newMediaBase)
      }
      payload.basicInfo = basicInfo
      payload.translations = translationsInput
    } else {
      const normalizedPayload = normalizeSavePayloadMediaToCurrentBase(payload, {
        persistedOldBases: [oldMediaBase],
        currentMediaBase: newMediaBase,
      }) as Record<string, unknown>
      basicInfo =
        normalizedPayload.basicInfo && typeof normalizedPayload.basicInfo === "object"
          ? (normalizedPayload.basicInfo as Record<string, unknown>)
          : basicInfo
      translationsInput =
        normalizedPayload.translations && typeof normalizedPayload.translations === "object"
          ? (normalizedPayload.translations as Record<string, unknown>)
          : translationsInput
      if (Array.isArray(normalizedPayload.quickStats)) {
        payload.quickStats = normalizedPayload.quickStats
      }
      payload.basicInfo = basicInfo
      payload.translations = translationsInput
    }

    let translations = applyProcedureTranslationsFormDefaults(
      normalizeProcedureSharedImages(translationsInput) as Record<string, unknown>
    )

    const isFeatured = Boolean(payload.isFeatured)
    const rating = parseNumber(basicInfo.rating)
    const reviewCount = Math.max(0, Math.trunc(parseNumber(basicInfo.reviewCount)))
    let image = parseString(basicInfo.mainImage)
    let imagePublicId = parseString(basicInfo.mainImagePublicId)
    let quickStats = Array.isArray(payload.quickStats) ? payload.quickStats : []
    const canonicalProcedureMedia = normalizePersistedMediaUrls({
      image,
      imagePublicId,
      translations,
      quickStats,
      basicInfo,
    }) as {
      image: string
      imagePublicId: string
      translations: Record<string, unknown>
      quickStats: unknown
      basicInfo: Record<string, unknown>
    }
    image = typeof canonicalProcedureMedia.image === "string" ? canonicalProcedureMedia.image.trim() : ""
    imagePublicId =
      typeof canonicalProcedureMedia.imagePublicId === "string"
        ? canonicalProcedureMedia.imagePublicId.trim()
        : ""
    translations = canonicalProcedureMedia.translations as typeof translations
    quickStats = Array.isArray(canonicalProcedureMedia.quickStats) ? canonicalProcedureMedia.quickStats : []
    basicInfo = {
      ...canonicalProcedureMedia.basicInfo,
      mainImage: image,
      mainImagePublicId: imagePublicId,
    }
    const en = getLocaleData(translations, "en")
    const mergedStayCity = parseString(basicInfo.stayCity) || parseString(en.stayCity)
    const basicInfoJson = { ...basicInfo, slug, name: en.name, description: en.description, stayCity: mergedStayCity }

    await prisma.procedure.update({
      where: { id },
      data: {
        categoryId,
        slug,
        name: en.name || existing.name,
        description: en.description || existing.description,
        image,
        imagePublicId,
        rating,
        reviewCount,
        isFeatured,
        status,
        basicInfoJson: toJson(basicInfoJson),
        quickStatsJson: toJson(quickStats),
        overviewJson: toJson(en.overview),
        countriesComparisonJson: toJson(en.countriesComparison),
        processJson: toJson(en.process),
        benefitsJson: toJson(en.benefits),
        beforeAfterSectionJson: toJson(en.beforeAfterSection),
        doctorsSectionJson: toJson({ ...en.doctorsSection, stayCity: en.stayCity }),
        recoveryJson: toJson(en.recovery),
        packageSectionJson: toJson(en.packageSection),
        reviewsSectionJson: toJson(en.reviewsSection),
        faqSectionJson: toJson(en.faqSection),
        relatedJson: toJson(en.related),
        ctaJson: toJson(en.cta),
        videoSectionJson: toJson(en.videoSection),
        ...contentAuditUpdateData(auth.user.id),
      } as any,
    })

    for (const locale of PROCEDURE_LOCALES) {
      const localeDataRaw = getLocaleData(translationsInput, locale)
      if (locale !== "en" && !hasMeaningfulLocaleTranslation(localeDataRaw)) {
        await prisma.procedureTranslation.deleteMany({ where: { procedureId: id, locale } })
        continue
      }

      const localeData = getLocaleData(translations, locale)

      await prisma.procedureTranslation.upsert({
        where: { procedureId_locale: { procedureId: id, locale } },
        update: {
          ...touchUpdatedAt(),
          name: localeData.name || (locale === "en" ? en.name : null),
          description: localeData.description || (locale === "en" ? en.description : null),
          stayCity: parseString(localeData.stayCity) || null,
          basicInfoJson: toJson(localeData.basicInfo),
          overviewJson: toJson(localeData.overview),
          countriesComparisonJson: toJson(localeData.countriesComparison),
          processJson: toJson(localeData.process),
          benefitsJson: toJson(localeData.benefits),
          beforeAfterSectionJson: toJson(localeData.beforeAfterSection),
          doctorsSectionJson: toJson({ ...localeData.doctorsSection, stayCity: localeData.stayCity }),
          recoveryJson: toJson(localeData.recovery),
          packageSectionJson: toJson(localeData.packageSection),
          reviewsSectionJson: toJson(localeData.reviewsSection),
          faqSectionJson: toJson(localeData.faqSection),
          relatedJson: toJson(localeData.related),
          ctaJson: toJson(localeData.cta),
          videoSectionJson: toJson(localeData.videoSection),
        } as any,
        create: {
          id: randomUUID(),
          updatedAt: new Date(),
          procedureId: id,
          locale,
          name: localeData.name || (locale === "en" ? en.name : null),
          description: localeData.description || (locale === "en" ? en.description : null),
          stayCity: parseString(localeData.stayCity) || null,
          basicInfoJson: toJson(localeData.basicInfo),
          overviewJson: toJson(localeData.overview),
          countriesComparisonJson: toJson(localeData.countriesComparison),
          processJson: toJson(localeData.process),
          benefitsJson: toJson(localeData.benefits),
          beforeAfterSectionJson: toJson(localeData.beforeAfterSection),
          doctorsSectionJson: toJson({ ...localeData.doctorsSection, stayCity: localeData.stayCity }),
          recoveryJson: toJson(localeData.recovery),
          packageSectionJson: toJson(localeData.packageSection),
          reviewsSectionJson: toJson(localeData.reviewsSection),
          faqSectionJson: toJson(localeData.faqSection),
          relatedJson: toJson(localeData.related),
          ctaJson: toJson(localeData.cta),
          videoSectionJson: toJson(localeData.videoSection),
        } as any,
      })
    }

    const updated = await prisma.procedure.findUnique({
      where: { id },
      include: {
        Category: { select: { id: true, name: true, color: true } },
        ...contentAuditProcedureInclude,
      },
    })
    if (!updated) return fail("Procedure not found", 404)
    const updatedTranslations = await prisma.procedureTranslation.findMany({
      where: { procedureId: id },
      select: {
        locale: true,
        name: true,
        description: true,
        stayCity: true,
        basicInfoJson: true,
        overviewJson: true,
        countriesComparisonJson: true,
        processJson: true,
        benefitsJson: true,
        beforeAfterSectionJson: true,
        doctorsSectionJson: true,
        recoveryJson: true,
        packageSectionJson: true,
        reviewsSectionJson: true,
        faqSectionJson: true,
        relatedJson: true,
        ctaJson: true,
        videoSectionJson: true,
      } as any,
    })

    const savedForm = buildFormFromDb(
      updated as unknown as Record<string, unknown>,
      updatedTranslations as unknown as Array<Record<string, unknown>>
    )

    await reconcileEntityMediaAfterSave({
      namespaceChanged: slugChanged,
      oldBases: [oldMediaBase],
      newMediaBase,
      previousMediaKeys: previousSnapshot.ids,
      savedPayload: savedForm,
      collectNextMediaKeys: collectProcedureImagePublicIds,
    })

    const { Category, ...updatedBase } = updated
    return NextResponse.json({
      success: true as const,
      procedure: {
        ...updatedBase,
        category: Category ?? null,
        form: savedForm,
        audit: buildContentAudit(updated),
      },
    })
  } catch (err) {
    console.error("[procedures] PUT error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to update procedure" }, { status: 500 })
  }
}

export async function DELETE(_req: NextRequest, context: RouteContext) {
  try {
    const { id } = await context.params
    if (!id) return fail("Missing id")
    const existing = await prisma.procedure.findUnique({
      where: { id },
      select: {
        id: true,
        categoryId: true,
        slug: true,
        name: true,
        description: true,
        image: true,
        imagePublicId: true,
        rating: true,
        reviewCount: true,
        isFeatured: true,
        status: true,
        basicInfoJson: true,
        quickStatsJson: true,
        overviewJson: true,
        countriesComparisonJson: true,
        processJson: true,
        benefitsJson: true,
        beforeAfterSectionJson: true,
        doctorsSectionJson: true,
        recoveryJson: true,
        packageSectionJson: true,
        reviewsSectionJson: true,
        faqSectionJson: true,
        relatedJson: true,
        ctaJson: true,
      },
    })
    if (!existing) return fail("Procedure not found", 404)

    const existingTranslations = await prisma.procedureTranslation.findMany({
      where: { procedureId: id },
      select: {
        locale: true,
        name: true,
        description: true,
        stayCity: true,
        overviewJson: true,
        countriesComparisonJson: true,
        processJson: true,
        benefitsJson: true,
        beforeAfterSectionJson: true,
        doctorsSectionJson: true,
        recoveryJson: true,
        packageSectionJson: true,
        reviewsSectionJson: true,
        faqSectionJson: true,
        relatedJson: true,
        ctaJson: true,
      },
    })

    const snapshot = buildProcedureImageSnapshot(
      existing as unknown as Record<string, unknown>,
      existingTranslations as unknown as Array<Record<string, unknown>>
    )

    await prisma.procedureTranslation.deleteMany({ where: { procedureId: id } })
    await prisma.procedure.delete({ where: { id } })

    if (snapshot.ids.size > 0) {
      await Promise.allSettled([...snapshot.ids].map((pid) => deleteImage(pid)))
    }
    for (const folder of snapshot.folders) {
      await purgePrefixExcept(folder, [])
      await deletePrefixIfEmpty(folder)
    }

    return NextResponse.json({ success: true as const })
  } catch (err) {
    console.error("[procedures] DELETE error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to delete procedure" }, { status: 500 })
  }
}
