import { randomUUID } from "crypto"
import { NextRequest, NextResponse } from "next/server"
import type { Doctor_status, Prisma } from "@prisma/client"
import {
  prisma,
  PRISMA_INTERACTIVE_TX_MAX_WAIT_MS,
  PRISMA_INTERACTIVE_TX_TIMEOUT_MS,
} from "@/lib/prisma"
import { deleteImage } from "@/lib/r2-storage"
import { relocateMediaBases, rewriteMediaPathsMulti } from "@/lib/media-manager/slug-relocate"
import { doctorMediaBaseForName } from "@/lib/media-manager/paths"
import { normalizePersistedMediaUrls } from "@/lib/normalize-persisted-media-urls"
import {
  collectManagedMediaKeysFromPayload,
  normalizeSavePayloadMediaToCurrentBase,
  reconcileEntityMediaAfterSave,
} from "@/lib/media-manager/slug-media-reconcile"
import { parseDoctorTagsFromBody } from "@/lib/doctor-tags"
import { resolveUniqueDoctorSlugForUpdate } from "@/lib/doctor-slug"
import {
  DOCTOR_LOCALES,
  buildDoctorTranslations,
  parseDoctorListFieldFromDb,
  parseDoctorLocale,
  parseDoctorPayloadTranslations,
  resolveDoctorTranslation,
  serializeDoctorListFieldForDb,
  serializeDoctorLongTextJson,
  unwrapDoctorLongTextJson,
} from "@/lib/doctor-db"
import { getPersistedMediaValidationError } from "@/lib/media-manager/validate-persisted-payload"
import {
  buildContentAudit,
  contentAuditUpdateData,
  touchUpdatedAt,
  requireContentAuditActor,
} from "@/lib/content-audit"
import { contentAuditUserSelect } from "@/types/content-audit"

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

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

const NO_CACHE_HEADERS = {
  "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
  Pragma: "no-cache",
  Expires: "0",
}

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

function parseIsFeatured(value: unknown): boolean {
  if (typeof value === "boolean") return value
  if (value === "true" || value === 1) return true
  if (value === "false" || value === 0) return false
  return false
}

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

function parseIntValue(value: unknown): number {
  const n = typeof value === "number" ? value : Number(value)
  if (!Number.isFinite(n)) return 0
  return Math.max(0, Math.trunc(n))
}

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

function parseLanguages(value: unknown): string[] {
  if (!Array.isArray(value)) return []
  const out: string[] = []
  const seen = new Set<string>()
  for (const item of value) {
    if (typeof item !== "string") continue
    const normalized = item.trim().replace(/\s+/g, " ")
    if (!normalized) continue
    const key = normalized.toLowerCase()
    if (seen.has(key)) continue
    seen.add(key)
    out.push(normalized)
  }
  return out
}

function parseStringList(value: unknown): string[] {
  if (!Array.isArray(value)) return []
  return value
    .filter((v): v is string => typeof v === "string")
    .map((v) => v.trim())
    .filter((v) => v !== "")
}

function parseCredentials(value: unknown) {
  if (!value || typeof value !== "object") {
    return {
      education: [] as string[],
      certifications: [] as string[],
      researchPublications: [] as string[],
      internationalExperience: [] as string[],
    }
  }
  const source = value as {
    education?: unknown
    certifications?: unknown
    researchPublications?: unknown
    internationalExperience?: unknown
  }
  return {
    education: parseStringList(source.education),
    certifications: parseStringList(source.certifications),
    researchPublications: parseStringList(source.researchPublications),
    internationalExperience: parseStringList(source.internationalExperience),
  }
}

function parseSpecialties(value: unknown) {
  value = unwrapDoctorLongTextJson(value)
  if (!Array.isArray(value)) return []
  return value
    .map((item) => {
      if (!item || typeof item !== "object") return null
      const source = item as { title?: unknown; description?: unknown; iconKey?: unknown; text?: unknown }
      const titleRaw = typeof source.title === "string" ? source.title : typeof source.text === "string" ? source.text : ""
      const title = titleRaw.trim()
      const iconKey = typeof source.iconKey === "string" ? source.iconKey.trim() : ""
      const description = typeof source.description === "string" ? source.description.trim() : ""
      if (!title || !iconKey || !description) return null
      return { title, description, iconKey }
    })
    .filter((item): item is { title: string; description: string; iconKey: string } => item !== null)
}

function parseCases(value: unknown) {
  value = unwrapDoctorLongTextJson(value)
  if (!Array.isArray(value)) return []
  return value
    .map((item) => {
      if (!item || typeof item !== "object") return null
      const source = item as {
        image?: unknown
        imagePublicId?: unknown
        procedureType?: unknown
        graftsLabel?: unknown
        grafts?: unknown
        monthsPostOpLabel?: unknown
        monthsPostOp?: unknown
        ageLabel?: unknown
        age?: unknown
        satisfactionLabel?: unknown
        satisfaction?: unknown
      }
      const image = parseString(source.image)
      const imagePublicId = parseString(source.imagePublicId)
      const procedureType = parseString(source.procedureType)
      if (!image || !imagePublicId) return null
      const graftsLabel = parseString(source.graftsLabel) || "Grafts"
      const grafts = parseString(source.grafts)
      const monthsPostOpLabel = parseString(source.monthsPostOpLabel) || "Months Post-Op"
      const monthsPostOp = parseString(source.monthsPostOp)
      const ageLabel = parseString(source.ageLabel) || "Age"
      const age = parseString(source.age)
      const satisfactionLabel = parseString(source.satisfactionLabel) || "Satisfaction"
      const satisfaction = parseString(source.satisfaction)
      return {
        image,
        imagePublicId,
        procedureType,
        graftsLabel,
        grafts,
        monthsPostOpLabel,
        monthsPostOp,
        ageLabel,
        age,
        satisfactionLabel,
        satisfaction,
      }
    })
    .filter((item): item is {
      image: string
      imagePublicId: string
      procedureType: string
      graftsLabel: string
      grafts: string
      monthsPostOpLabel: string
      monthsPostOp: string
      ageLabel: string
      age: string
      satisfactionLabel: string
      satisfaction: string
    } => item !== null)
}

function parseReviews(value: unknown) {
  value = unwrapDoctorLongTextJson(value)
  if (!Array.isArray(value)) return []
  return value
    .map((item) => {
      if (!item || typeof item !== "object") return null
      const source = item as {
        patientName?: unknown
        name?: unknown
        country?: unknown
        treatment?: unknown
        date?: unknown
        content?: unknown
        rating?: unknown
      }
      const patientNameRaw = typeof source.patientName === "string" ? source.patientName : typeof source.name === "string" ? source.name : ""
      const patientName = patientNameRaw.trim()
      const country = parseString(source.country)
      const treatment = parseString(source.treatment)
      const content = parseString(source.content)
      if (!patientName || !country || !treatment || !content) return null

      const dateRaw = parseString(source.date)
      const date = dateRaw ? new Date(dateRaw) : null
      const validDate = date && !Number.isNaN(date.getTime()) ? date.toISOString().slice(0, 10) : ""
      const rating =
        source.rating === "" || source.rating === null || source.rating === undefined
          ? null
          : parseFloatValue(source.rating)

      return {
        patientName,
        country,
        treatment,
        date: validDate,
        content,
        rating: rating !== null && rating > 0 ? rating : null,
      }
    })
    .filter((item): item is {
      patientName: string
      country: string
      treatment: string
      date: string
      content: string
      rating: number | null
    } => item !== null)
}

function parseFaq(value: unknown) {
  value = unwrapDoctorLongTextJson(value)
  if (!Array.isArray(value)) return []
  return value
    .map((item) => {
      if (!item || typeof item !== "object") return null
      const source = item as { question?: unknown; answer?: unknown }
      const question = parseString(source.question)
      const answer = parseString(source.answer)
      if (!question || !answer) return null
      return { question, answer }
    })
    .filter((item): item is { question: string; answer: string } => item !== null)
}

function credentialRelationRows(doctor: { credentials?: { type: string; text: string }[] }) {
  return Array.isArray(doctor.credentials) ? doctor.credentials : []
}

function specialtyRelationRows(
  doctor: { specialties?: { title: string; description: string | null; iconKey: string }[] }
) {
  return Array.isArray(doctor.specialties) ? doctor.specialties : []
}

function beforeAfterRelationRows(
  doctor: {
    beforeAfter?: {
      image: string
      imagePublicId: string
      procedureType: string
      grafts: string
      monthsPostOp: string
      age: string
      satisfaction: string
    }[]
  }
) {
  return Array.isArray(doctor.beforeAfter) ? doctor.beforeAfter : []
}

function reviewRelationRows(
  doctor: {
    reviews?: {
      patientName: string
      country: string
      treatment: string
      date: Date | null
      content: string
      rating: number | null
    }[]
  }
) {
  return Array.isArray(doctor.reviews) ? doctor.reviews : []
}

function faqRelationRows(doctor: { faq?: { question: string; answer: string }[] }) {
  return Array.isArray(doctor.faq) ? doctor.faq : []
}

function parseCredentialsJson(value: unknown): {
  education: string[]
  certifications: string[]
  researchPublications: string[]
  internationalExperience: string[]
} {
  value = unwrapDoctorLongTextJson(value)
  if (!value || typeof value !== "object") {
    return {
      education: [],
      certifications: [],
      researchPublications: [],
      internationalExperience: [],
    }
  }
  const source = value as {
    education?: unknown
    certifications?: unknown
    researchPublications?: unknown
    internationalExperience?: unknown
  }
  return {
    education: parseStringList(source.education),
    certifications: parseStringList(source.certifications),
    researchPublications: parseStringList(source.researchPublications),
    internationalExperience: parseStringList(source.internationalExperience),
  }
}

const doctorAuditInclude = {
  User_Doctor_createdByIdToUser: { select: contentAuditUserSelect },
  User_Doctor_updatedByIdToUser: { select: contentAuditUserSelect },
} satisfies Prisma.DoctorInclude

const doctorInclude = {
  Category: { select: { id: true, name: true, slug: true, color: true } },
  ...doctorAuditInclude,
} satisfies Prisma.DoctorInclude

function doctorForContentAudit(doctor: {
  createdAt: Date
  updatedAt: Date
  User_Doctor_createdByIdToUser?: {
    id: string
    name: string | null
    email: string
    image: string | null
  } | null
  User_Doctor_updatedByIdToUser?: {
    id: string
    name: string | null
    email: string
    image: string | null
  } | null
  createdBy?: { id: string; name: string | null; email: string; image: string | null } | null
  updatedBy?: { id: string; name: string | null; email: string; image: string | null } | null
}) {
  return {
    createdAt: doctor.createdAt,
    updatedAt: doctor.updatedAt,
    createdBy: doctor.User_Doctor_createdByIdToUser ?? doctor.createdBy ?? null,
    updatedBy: doctor.User_Doctor_updatedByIdToUser ?? doctor.updatedBy ?? null,
  }
}

/** After PUT we clear legacy relation rows — avoid loading empty joins (latency + tx timeout). */
const doctorPutSelect = {
  id: true,
  name: true,
  slug: true,
  categoryId: true,
  Category: { select: { id: true, name: true, slug: true, color: true } },
  image: true,
  imagePublicId: true,
  rating: true,
  reviewCount: true,
  description: true,
  experienceYears: true,
  casesCount: true,
  successRate: true,
  nationalities: true,
  languages: true,
  tags: true,
  isFeatured: true,
  status: true,
  createdAt: true,
  updatedAt: true,
  credentialsJson: true,
  specialtiesJson: true,
  beforeAfterJson: true,
  reviewsJson: true,
  faqJson: true,
  credentialsHeadingJson: true,
  biographyHeadingJson: true,
  specialtiesHeadingJson: true,
  beforeAfterHeadingJson: true,
  reviewsHeadingJson: true,
  faqHeadingJson: true,
} satisfies Prisma.DoctorSelect

function toDoctorResponse(doctor: any,
  localized?: ReturnType<typeof buildDoctorTranslations>["en"],
  translations?: ReturnType<typeof buildDoctorTranslations>
) {
  const credRows = credentialRelationRows(doctor)
  const baseCredentials =
    doctor.credentialsJson !== null && doctor.credentialsJson !== undefined
      ? parseCredentialsJson(doctor.credentialsJson)
      : { education: [], certifications: [], researchPublications: [], internationalExperience: [] }
  const specRows = specialtyRelationRows(doctor)
  const baseSpecialties =
    doctor.specialtiesJson !== null && doctor.specialtiesJson !== undefined ? parseSpecialties(doctor.specialtiesJson) : []
  const baRows = beforeAfterRelationRows(doctor)
  const baseBeforeAfter =
    doctor.beforeAfterJson !== null && doctor.beforeAfterJson !== undefined ? parseCases(doctor.beforeAfterJson) : []
  const revRows = reviewRelationRows(doctor)
  const baseReviews =
    doctor.reviewsJson !== null && doctor.reviewsJson !== undefined ? parseReviews(doctor.reviewsJson) : []
  const fqRows = faqRelationRows(doctor)
  const baseFaq = doctor.faqJson !== null && doctor.faqJson !== undefined ? parseFaq(doctor.faqJson) : []

  return normalizePersistedMediaUrls({
    id: doctor.id,
    name: localized?.name || doctor.name,
    slug: doctor.slug,
    categoryId: doctor.categoryId,
    category: doctor.Category ?? doctor.category ?? null,
    image: doctor.image,
    imagePublicId: doctor.imagePublicId,
    rating: doctor.rating,
    reviewCount: doctor.reviewCount,
    description: localized?.description ?? doctor.description,
    experienceYears: doctor.experienceYears,
    casesCount: doctor.casesCount,
    successRate: doctor.successRate,
    nationalities: doctor.nationalities,
    languages: localized?.languages.length ? localized.languages : parseDoctorListFieldFromDb(doctor.languages),
    tags: localized?.tags.length ? localized.tags : parseDoctorListFieldFromDb(doctor.tags),
    isFeatured: doctor.isFeatured,
    status: doctor.status,
    biography: localized?.biography ?? "",
    doctorStatement: localized?.doctorStatement ?? "",
    biographyHeading: localized?.biographyHeading ?? (doctor as any).biographyHeadingJson ?? null,
    credentials: parseCredentials(localized?.credentials ?? baseCredentials),
    credentialsHeading: localized?.credentialsHeading ?? (doctor as any).credentialsHeadingJson ?? null,
    specialties: parseSpecialties(localized?.specialties ?? baseSpecialties),
    specialtiesHeading: localized?.specialtiesHeading ?? (doctor as any).specialtiesHeadingJson ?? null,
    beforeAfter: parseCases(localized?.beforeAfter ?? baseBeforeAfter),
    beforeAfterHeading: localized?.beforeAfterHeading ?? (doctor as any).beforeAfterHeadingJson ?? null,
    reviews: parseReviews(localized?.reviews ?? baseReviews),
    reviewsHeading: localized?.reviewsHeading ?? (doctor as any).reviewsHeadingJson ?? null,
    faq: parseFaq(localized?.faq ?? baseFaq),
    faqHeading: localized?.faqHeading ?? (doctor as any).faqHeadingJson ?? null,
    translations,
    audit: buildContentAudit(doctorForContentAudit(doctor)),
  })
}

export async function GET(_req: NextRequest, context: RouteContext) {
  try {
    const locale = parseDoctorLocale(_req.nextUrl.searchParams.get("locale"))
    const { id } = await context.params
    if (!id) return fail("Missing id")

    let doctor = null as any
    try {
      doctor = await prisma.doctor.findUnique({
        where: { id },
        include: doctorInclude,
      })
    } catch (err) {
      console.warn("[doctors] GET [id] primary query failed, trying compatibility fallback:", err)
      doctor = await prisma.doctor.findUnique({
        where: { id },
        include: {
          Category: { select: { id: true, name: true, slug: true, color: true } },
        },
      })
    }
    if (!doctor) return fail("Doctor not found", 404)
    const translationRows = await prisma.doctorTranslation.findMany({
      where: { doctorId: id },
      select: {
        locale: true,
        name: true,
        description: true,
        credentials: true,
        credentialsHeading: true,
        biography: true,
        biographyHeading: true,
        doctorStatement: true,
        specialties: true,
        specialtiesHeading: true,
        beforeAfter: true,
        beforeAfterHeading: true,
        reviews: true,
        reviewsHeading: true,
        faq: true,
        faqHeading: true,
        tags: true,
        languages: true,
      },
    })
    const translations = buildDoctorTranslations(
      {
        name: doctor.name,
        description: doctor.description,
        credentials: doctor.credentialsJson,
        biography: null,
        doctorStatement: null,
        specialties: doctor.specialtiesJson,
        beforeAfter: doctor.beforeAfterJson,
        reviews: doctor.reviewsJson,
        faq: doctor.faqJson,
        tags: doctor.tags,
        languages: doctor.languages,
      },
      translationRows
    )
    const localized = resolveDoctorTranslation(translations, locale)

    return NextResponse.json(
      { success: true as const, doctor: toDoctorResponse(doctor, localized, translations) },
      { headers: NO_CACHE_HEADERS }
    )
  } catch (err) {
    console.error("[doctors] GET [id] error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to load doctor" }, { 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.doctor.findUnique({
      where: { id },
      select: {
        id: true,
        name: true,
        categoryId: true,
        imagePublicId: true,
        beforeAfterJson: true,
      },
    })
    if (!existing) return fail("Doctor not found", 404)

    const previousMediaKeys = collectManagedMediaKeysFromPayload({
      imagePublicId: existing.imagePublicId,
      beforeAfter:
        existing.beforeAfterJson !== null && existing.beforeAfterJson !== undefined
          ? parseCases(existing.beforeAfterJson)
          : [],
    })

    const body = await req.json()
    const mediaError = getPersistedMediaValidationError(body)
    if (mediaError) return fail(mediaError)

    const payload = body as Record<string, unknown>
    const translations = parseDoctorPayloadTranslations(payload)
    const status: Doctor_status = body.status === "draft" ? "draft" : "published"
    const isDraft = status === "draft"
    const categoryId = parseString(body.categoryId)
    const name = translations.en.name || parseString(body.name)
    const image = parseString(body.image)
    const imagePublicId = parseString(body.imagePublicId)
    const rating = parseFloatValue(body.rating)
    const reviewCount = parseIntValue(body.reviewCount)
    let description =
      translations.en.description ?? (typeof body.description === "string" ? body.description.trim() : "")
    const experienceYears = parseIntValue(body.experienceYears)
    const casesCount = parseString(body.casesCount)
    const successRate = parseString(body.successRate)
    const nationalities = parseString(body.nationalities)
    const languages = parseLanguages(body.languages)
    const tags = parseDoctorTagsFromBody(body.tags)
    const isFeatured = parseIsFeatured(body.isFeatured)
    const biography = typeof body.biography === "string" ? body.biography.trim() : ""
    const doctorStatement = typeof body.doctorStatement === "string" ? body.doctorStatement.trim() : ""
    const credentialSource = body.credentials && typeof body.credentials === "object"
      ? (body.credentials as {
          education?: unknown
          certifications?: unknown
          researchPublications?: unknown
          internationalExperience?: unknown
        })
      : {}
    const education = parseStringList(credentialSource.education)
    const certifications = parseStringList(credentialSource.certifications)
    const researchPublications = parseStringList(credentialSource.researchPublications)
    const internationalExperience = parseStringList(credentialSource.internationalExperience)
    let credentials = parseCredentials(body.credentials)
    let specialties = parseSpecialties(body.specialties)
    const beforeAfter = parseCases(body.beforeAfter)
    const reviews = parseReviews(body.reviews)
    let faq = parseFaq(body.faq)

    let effectiveCategoryId = categoryId
    let effectiveName = name

    if (isDraft) {
      if (!effectiveCategoryId) {
        effectiveCategoryId = existing.categoryId
      } else {
        const selectedCategory = await prisma.category.findUnique({
          where: { id: effectiveCategoryId },
          select: { id: true },
        })
        if (!selectedCategory) {
          effectiveCategoryId = existing.categoryId
        }
      }
      if (!effectiveName) {
        effectiveName = existing.name || "Untitled Doctor Draft"
      }
    } else {
      if (!effectiveCategoryId) return fail("Category is required")
      if (!effectiveName) return fail("Doctor name is required")
    }

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

    const slug = await resolveUniqueDoctorSlugForUpdate(effectiveName, id)

    const oldDoctorMediaBase = doctorMediaBaseForName(existing.name)
    const newDoctorMediaBase = doctorMediaBaseForName(effectiveName)
    const nameChanged = oldDoctorMediaBase !== newDoctorMediaBase
    let imageOut = image
    let imagePublicIdOut = imagePublicId
    let beforeAfterOut = beforeAfter
    let reviewsOut = reviews
    let translationsOut = translations
    if (nameChanged) {
      try {
        await relocateMediaBases([oldDoctorMediaBase], newDoctorMediaBase)
      } catch (relocateErr) {
        console.error("[doctors] name media relocate failed:", relocateErr)
        return fail("Failed to move doctor images for the new name", 500)
      }
      const bundle = JSON.parse(
        JSON.stringify({
          image: imageOut,
          imagePublicId: imagePublicIdOut,
          beforeAfter: beforeAfterOut,
          reviews: reviewsOut,
          translations: translationsOut,
        })
      ) as {
        image: string
        imagePublicId: string
        beforeAfter: unknown
        reviews: unknown
        translations: typeof translationsOut
      }
      const rewritten = rewriteMediaPathsMulti(bundle, [oldDoctorMediaBase], newDoctorMediaBase) as typeof bundle
      imageOut = typeof rewritten.image === "string" ? rewritten.image.trim() : ""
      imagePublicIdOut = typeof rewritten.imagePublicId === "string" ? rewritten.imagePublicId.trim() : ""
      beforeAfterOut = parseCases(rewritten.beforeAfter)
      reviewsOut = parseReviews(rewritten.reviews)
      translationsOut = rewritten.translations
      // Root `Doctor` JSON/text columns are filled from these locals — they were parsed
      // before rewrite, so sync from English translation payload which was rewritten in the bundle.
      const enOut = translationsOut.en
      description = enOut.description ?? description
      credentials = parseCredentials(enOut.credentials)
      specialties = parseSpecialties(enOut.specialties)
      faq = parseFaq(enOut.faq)
    } else {
      const bundle = JSON.parse(
        JSON.stringify({
          image: imageOut,
          imagePublicId: imagePublicIdOut,
          beforeAfter: beforeAfterOut,
          reviews: reviewsOut,
          translations: translationsOut,
        })
      ) as {
        image: string
        imagePublicId: string
        beforeAfter: unknown
        reviews: unknown
        translations: typeof translationsOut
      }
      const normalized = normalizeSavePayloadMediaToCurrentBase(bundle, {
        persistedOldBases: [oldDoctorMediaBase],
        currentMediaBase: newDoctorMediaBase,
      }) as typeof bundle
      imageOut = typeof normalized.image === "string" ? normalized.image.trim() : ""
      imagePublicIdOut = typeof normalized.imagePublicId === "string" ? normalized.imagePublicId.trim() : ""
      beforeAfterOut = parseCases(normalized.beforeAfter)
      reviewsOut = parseReviews(normalized.reviews)
      translationsOut = normalized.translations
    }

    const canonicalMedia = normalizePersistedMediaUrls({
      image: imageOut,
      imagePublicId: imagePublicIdOut,
      beforeAfter: beforeAfterOut,
      reviews: reviewsOut,
      translations: translationsOut,
    }) as {
      image: string
      imagePublicId: string
      beforeAfter: unknown
      reviews: unknown
      translations: typeof translationsOut
    }
    imageOut = typeof canonicalMedia.image === "string" ? canonicalMedia.image.trim() : ""
    imagePublicIdOut =
      typeof canonicalMedia.imagePublicId === "string" ? canonicalMedia.imagePublicId.trim() : ""
    beforeAfterOut = parseCases(canonicalMedia.beforeAfter)
    reviewsOut = parseReviews(canonicalMedia.reviews)
    translationsOut = canonicalMedia.translations

    if (status === "published") {
      if (!imageOut) return fail("Doctor image is required")
      if (!imagePublicIdOut) return fail("Doctor image public id is required")
      if (!description) return fail("Description is required")
      if (rating <= 0 || rating > 5) return fail("Rating is required and must be between 0 and 5")
      if (reviewCount <= 0) return fail("Review count is required and must be greater than 0")
      if (experienceYears <= 0) return fail("Experience (Years) is required and must be greater than 0")
      if (!casesCount) return fail("Cases Count is required")
      if (!successRate) return fail("Success Rate is required")
      if (!nationalities) return fail("Nationalities is required")
      if (languages.length === 0) return fail("At least one language is required")
      if (!education.length) return fail("Education is required")
      if (!certifications.length) return fail("Certifications is required")
      if (!researchPublications.length) return fail("Research & Publications is required")
      if (!internationalExperience.length) return fail("International Experience is required")
      if (!specialties.length) return fail("Surgical Specialties is required")
      if (!doctorStatement) return fail("Doctor Statement is required")
      if (!biography) return fail("Biography is required")
      if (!reviewsOut.length) return fail("Patient Reviews is required")
      if (!faq.length) return fail("Frequently Asked Questions is required")
    }

    const result = await prisma.$transaction(
      async (tx) => {
      const translationNow = new Date()

      const updatedDoctor = await tx.doctor.update({
        where: { id },
        data: {
          categoryId: effectiveCategoryId,
          name: effectiveName,
          slug,
          image: imageOut,
          imagePublicId: imagePublicIdOut,
          rating,
          reviewCount,
          description,
          experienceYears,
          casesCount,
          successRate,
          nationalities,
          languages: serializeDoctorListFieldForDb(languages),
          tags: serializeDoctorListFieldForDb(tags),
          isFeatured,
          status,
          credentialsJson: serializeDoctorLongTextJson(credentials),
          credentialsHeadingJson: serializeDoctorLongTextJson(body.credentialsHeading),
          biographyHeadingJson: serializeDoctorLongTextJson(body.biographyHeading),
          specialtiesJson: serializeDoctorLongTextJson(specialties),
          specialtiesHeadingJson: serializeDoctorLongTextJson(body.specialtiesHeading),
          beforeAfterJson: serializeDoctorLongTextJson(beforeAfterOut),
          beforeAfterHeadingJson: serializeDoctorLongTextJson(body.beforeAfterHeading),
          reviewsJson: serializeDoctorLongTextJson(reviewsOut),
          reviewsHeadingJson: serializeDoctorLongTextJson(body.reviewsHeading),
          faqJson: serializeDoctorLongTextJson(faq),
          faqHeadingJson: serializeDoctorLongTextJson(body.faqHeading),
          ...contentAuditUpdateData(auth.user.id),
        },
        select: doctorPutSelect,
      })
      const payloadByLocale = { en: translationsOut.en, ar: translationsOut.ar, tr: translationsOut.tr }
      for (const localeCode of DOCTOR_LOCALES) {
        const trData = payloadByLocale[localeCode]
        const trCredentials = parseCredentials(trData.credentials)
        const trSpecialties = parseSpecialties(trData.specialties)
        const trBeforeAfter = parseCases(trData.beforeAfter)
        const trReviews = parseReviews(trData.reviews)
        const trFaq = parseFaq(trData.faq)
        const hasTranslatedSections =
          trData.biography !== null ||
          trData.doctorStatement !== null ||
          trCredentials.education.length > 0 ||
          trCredentials.certifications.length > 0 ||
          trCredentials.researchPublications.length > 0 ||
          trCredentials.internationalExperience.length > 0 ||
          trSpecialties.length > 0 ||
          trBeforeAfter.length > 0 ||
          trReviews.length > 0 ||
          trFaq.length > 0 ||
          trData.tags.length > 0 ||
          trData.languages.length > 0
        if (localeCode !== "en" && !trData.name && !trData.description && !hasTranslatedSections) {
          await tx.doctorTranslation.deleteMany({ where: { doctorId: id, locale: localeCode } })
          continue
        }
        await tx.doctorTranslation.upsert({
          where: { doctorId_locale: { doctorId: id, locale: localeCode } },
          update: {
            ...touchUpdatedAt(),
            name: trData.name || null,
            description: trData.description,
            credentials: serializeDoctorLongTextJson(trCredentials),
            credentialsHeading: serializeDoctorLongTextJson(trData.credentialsHeading),
            biography: trData.biography,
            biographyHeading: serializeDoctorLongTextJson(trData.biographyHeading),
            doctorStatement: trData.doctorStatement,
            specialties: serializeDoctorLongTextJson(trSpecialties),
            specialtiesHeading: serializeDoctorLongTextJson(trData.specialtiesHeading),
            beforeAfter: serializeDoctorLongTextJson(trBeforeAfter),
            beforeAfterHeading: serializeDoctorLongTextJson(trData.beforeAfterHeading),
            reviews: serializeDoctorLongTextJson(trReviews),
            reviewsHeading: serializeDoctorLongTextJson(trData.reviewsHeading),
            faq: serializeDoctorLongTextJson(trFaq),
            faqHeading: serializeDoctorLongTextJson(trData.faqHeading),
            tags: serializeDoctorListFieldForDb(trData.tags),
            languages: serializeDoctorListFieldForDb(trData.languages),
          },
          create: {
            id: randomUUID(),
            updatedAt: translationNow,
            doctorId: id,
            locale: localeCode,
            name: trData.name || null,
            description: trData.description,
            credentials: serializeDoctorLongTextJson(trCredentials),
            credentialsHeading: serializeDoctorLongTextJson(trData.credentialsHeading),
            biography: trData.biography,
            biographyHeading: serializeDoctorLongTextJson(trData.biographyHeading),
            doctorStatement: trData.doctorStatement,
            specialties: serializeDoctorLongTextJson(trSpecialties),
            specialtiesHeading: serializeDoctorLongTextJson(trData.specialtiesHeading),
            beforeAfter: serializeDoctorLongTextJson(trBeforeAfter),
            beforeAfterHeading: serializeDoctorLongTextJson(trData.beforeAfterHeading),
            reviews: serializeDoctorLongTextJson(trReviews),
            reviewsHeading: serializeDoctorLongTextJson(trData.reviewsHeading),
            faq: serializeDoctorLongTextJson(trFaq),
            faqHeading: serializeDoctorLongTextJson(trData.faqHeading),
            tags: serializeDoctorListFieldForDb(trData.tags),
            languages: serializeDoctorListFieldForDb(trData.languages),
          },
        })
      }
      const updatedTranslations = await tx.doctorTranslation.findMany({
        where: { doctorId: id },
        select: {
          locale: true,
          name: true,
          description: true,
          credentials: true,
          credentialsHeading: true,
          biographyHeading: true,
          biography: true,
          doctorStatement: true,
          specialties: true,
          specialtiesHeading: true,
          beforeAfter: true,
          beforeAfterHeading: true,
          reviews: true,
          reviewsHeading: true,
          faq: true,
          faqHeading: true,
          tags: true,
          languages: true,
        },
      })
      return { doctor: updatedDoctor, translations: updatedTranslations }
    },
    {
      maxWait: PRISMA_INTERACTIVE_TX_MAX_WAIT_MS,
      timeout: PRISMA_INTERACTIVE_TX_TIMEOUT_MS,
    }
    )

    await reconcileEntityMediaAfterSave({
      namespaceChanged: nameChanged,
      oldBases: [oldDoctorMediaBase],
      newMediaBase: newDoctorMediaBase,
      previousMediaKeys,
      savedPayload: {
        image: imageOut,
        imagePublicId: imagePublicIdOut,
        beforeAfter: beforeAfterOut,
        reviews: reviewsOut,
        translations: translationsOut,
      },
    })

    const auditRow = await prisma.doctor.findUnique({
      where: { id },
      select: {
        createdAt: true,
        updatedAt: true,
        User_Doctor_createdByIdToUser: { select: contentAuditUserSelect },
        User_Doctor_updatedByIdToUser: { select: contentAuditUserSelect },
      },
    })

    const responseTranslations = buildDoctorTranslations(
      {
        name: result.doctor.name,
        description: result.doctor.description,
        credentials: result.doctor.credentialsJson,
        credentialsHeading: (result.doctor as any).credentialsHeadingJson ?? null,
        biography: null,
        doctorStatement: null,
        biographyHeading: (result.doctor as any).biographyHeadingJson ?? null,
        specialties: result.doctor.specialtiesJson,
        specialtiesHeading: (result.doctor as any).specialtiesHeadingJson ?? null,
        beforeAfter: result.doctor.beforeAfterJson,
        beforeAfterHeading: (result.doctor as any).beforeAfterHeadingJson ?? null,
        reviews: result.doctor.reviewsJson,
        reviewsHeading: (result.doctor as any).reviewsHeadingJson ?? null,
        faq: result.doctor.faqJson,
        faqHeading: (result.doctor as any).faqHeadingJson ?? null,
        tags: result.doctor.tags,
        languages: result.doctor.languages,
      },
      result.translations
    )
    return NextResponse.json({
      success: true as const,
      doctor: toDoctorResponse(
        { ...result.doctor, ...(auditRow ?? { createdAt: new Date(), updatedAt: new Date() }) },
        responseTranslations.en,
        responseTranslations
      ),
    })
  } catch (err) {
    console.error("[doctors] PUT error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to update doctor" }, { 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.doctor.findUnique({
      where: { id },
      select: {
        imagePublicId: true,
        beforeAfterJson: true,
      },
    })
    if (!existing) return fail("Doctor not found", 404)

    const caseImagePublicIds =
      existing.beforeAfterJson !== null && existing.beforeAfterJson !== undefined
        ? parseCases(existing.beforeAfterJson).map((item) => item.imagePublicId)
        : []
    const imagePublicIds = [existing.imagePublicId, ...caseImagePublicIds].filter((publicId) => publicId.trim() !== "")

    await prisma.$transaction(async (tx) => {
      await tx.doctorTranslation.deleteMany({ where: { doctorId: id } })
      await tx.doctor.delete({ where: { id } })
    })
    if (imagePublicIds.length > 0) {
      await Promise.allSettled(imagePublicIds.map((publicId) => deleteImage(publicId)))
    }

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