import { randomUUID } from "crypto"
import { NextRequest, NextResponse } from "next/server"
import type { Procedure_status } from "@prisma/client"
import { prisma } from "@/lib/prisma"
import {
  buildContentAudit,
  contentAuditCreateData,
  contentAuditProcedureInclude,
  requireContentAuditActor,
} from "@/lib/content-audit"
import { applyProcedureTranslationsFormDefaults } from "@/lib/procedure-form-locale-defaults"
import {
  normalizeProcedureSharedImages,
  parseProcedureLocale,
  PROCEDURE_LOCALES,
} from "@/lib/procedure-db"
import { validateUrlSlug } from "@/lib/slug-validation"
import { getPersistedMediaValidationError } from "@/lib/media-manager/validate-persisted-payload"

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 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 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 toSummaryProcedure(row: {
  id: string
  name: string
  image: string
  rating: number
  reviewCount: number
  status: Procedure_status
  createdAt: Date
  updatedAt: Date
  isFeatured: boolean
  Category?: { name: string; color: string } | 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 {
    id: row.id,
    name: row.name,
    image: row.image,
    rating: row.rating,
    reviewCount: row.reviewCount,
    status: row.status,
    isFeatured: row.isFeatured,
    category: row.Category
      ? { name: row.Category.name, color: row.Category.color }
      : null,
    audit: buildContentAudit(row),
  }
}

export async function GET(req: NextRequest) {
  try {
    const locale = parseProcedureLocale(req.nextUrl.searchParams.get("locale"))
    let rows: Array<{
      id: string
      slug: string
      name: string
      image: string
      rating: number
      reviewCount: number
      status: Procedure_status
      isFeatured: boolean
      createdAt: Date
      updatedAt: Date
      Category?: { id: string; name: string; color: string } | 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
    }> = []

    try {
      rows = await prisma.procedure.findMany({
        orderBy: { updatedAt: "desc" },
        include: {
          Category: { select: { id: true, name: true, color: true } },
          ...contentAuditProcedureInclude,
        },
      })
    } catch (err) {
      if (!isSchemaDriftError(err)) {
        console.warn("[procedures] primary query failed, trying compatibility fallback:", err)
      }
      try {
        rows = await prisma.procedure.findMany({
          orderBy: { updatedAt: "desc" },
          include: {
            Category: { select: { id: true, name: true, color: true } },
          },
        })
      } catch (fallbackErr) {
        console.warn("[procedures] schema drift fallback -> raw query:", fallbackErr)
        const rawRows = await prisma.$queryRaw<Array<{
          id: string
          slug: string
          name: string
          image: string
          rating: number
          reviewCount: number
          status: Procedure_status
          isFeatured: number | boolean
          createdAt: Date
          updatedAt: Date
          categoryId: string | null
          categoryName: string | null
          categoryColor: string | null
        }>>`
          SELECT
            p.id,
            p.slug,
            p.name,
            p.image,
            p.rating,
            p.reviewCount,
            p.status,
            p.isFeatured,
            p.createdAt,
            p.updatedAt,
            c.id AS categoryId,
            c.name AS categoryName,
            c.color AS categoryColor
          FROM Procedure p
          LEFT JOIN Category c ON c.id = p.categoryId
          ORDER BY p.updatedAt DESC
        `
        rows = rawRows.map((r) => ({
          id: r.id,
          slug: r.slug,
          name: r.name,
          image: r.image,
          rating: Number(r.rating) || 0,
          reviewCount: Number(r.reviewCount) || 0,
          status: r.status,
          isFeatured: Boolean(r.isFeatured),
          createdAt: new Date(r.createdAt),
          updatedAt: new Date(r.updatedAt),
          Category: r.categoryId
            ? { id: r.categoryId, name: r.categoryName ?? "", color: r.categoryColor ?? "" }
            : null,
        }))
      }
    }

    const ids = rows.map((row) => row.id)
    const trRows = ids.length
      ? await prisma.procedureTranslation.findMany({
          where: { procedureId: { in: ids }, locale },
          select: { procedureId: true, locale: true, name: true },
        })
      : []
    const byProcedure = new Map<string, (typeof trRows)[number]>()
    for (const tr of trRows) byProcedure.set(tr.procedureId, tr)

    return NextResponse.json(
      {
        success: true as const,
        procedures: rows.map((row) => {
          const tr = byProcedure.get(row.id)
          return {
            ...toSummaryProcedure(row),
            slug: row.slug,
            name: parseString(tr?.name) || row.name,
          }
        }),
      },
      { headers: NO_CACHE_HEADERS }
    )
  } catch (err) {
    console.error("[procedures] GET error:", err)
    return NextResponse.json({ success: false as const, error: "Failed to load procedures" }, { status: 500 })
  }
}

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

    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 categoryId = parseString(payload.categoryId)
    const basicInfo =
      payload.basicInfo && typeof payload.basicInfo === "object"
        ? (payload.basicInfo as Record<string, unknown>)
        : {}
    const translationsInput =
      payload.translations && typeof payload.translations === "object"
        ? (payload.translations as Record<string, unknown>)
        : {}
    const translations = applyProcedureTranslationsFormDefaults(
      normalizeProcedureSharedImages(translationsInput) as Record<string, unknown>
    )
    const en = getLocaleData(translations, "en")

    const slug = parseString(basicInfo.slug)
    if (!slug) return fail("Slug is required")
    const slugValidation = validateUrlSlug(slug)
    if (!slugValidation.valid) return fail(slugValidation.error ?? "Invalid slug")

    const isFeatured = Boolean(payload.isFeatured)
    const rating = parseNumber(basicInfo.rating)
    const reviewCount = Math.max(0, Math.trunc(parseNumber(basicInfo.reviewCount)))
    const image = parseString(basicInfo.mainImage)
    const imagePublicId = parseString(basicInfo.mainImagePublicId)
    const quickStats = Array.isArray(payload.quickStats) ? payload.quickStats : []
    const mergedStayCity = parseString(basicInfo.stayCity) || parseString(en.stayCity)
    const basicInfoJson = {
      ...basicInfo,
      slug,
      name: en.name,
      description: en.description,
      stayCity: mergedStayCity,
      ...en.basicInfo,
    }

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

    const existingSlug = await prisma.procedure.findUnique({ where: { slug }, select: { id: true } })
    if (existingSlug) return fail("Slug already in use", 409)

    const hospitalFallbackCategory =
      categoryId ||
      (await prisma.category.findFirst({ select: { id: true }, orderBy: { createdAt: "asc" } }))?.id ||
      ""

    const now = new Date()
    const procedure = await prisma.procedure.create({
      data: {
        id: randomUUID(),
        categoryId: hospitalFallbackCategory,
        slug,
        name: en.name || "Untitled Procedure Draft",
        description: en.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),
        ...contentAuditCreateData(auth.user.id),
      },
      include: {
        Category: { select: { id: true, name: true, color: true } },
        ...contentAuditProcedureInclude,
      },
    })

    const translationRows = PROCEDURE_LOCALES.flatMap((locale) => {
      const localeDataRaw = getLocaleData(translationsInput, locale)
      if (locale !== "en" && !hasMeaningfulLocaleTranslation(localeDataRaw)) {
        return []
      }
      const localeData = getLocaleData(translations, locale)
      return [{
        id: randomUUID(),
        updatedAt: now,
        procedureId: procedure.id,
        locale,
        name: localeData.name || (locale === "en" ? procedure.name : null),
        description: localeData.description || (locale === "en" ? procedure.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),
      }]
    })

    if (translationRows.length > 0) {
      await prisma.procedureTranslation.createMany({ data: translationRows })
    }

    return NextResponse.json(
      {
        success: true as const,
        procedure: { ...toSummaryProcedure(procedure), slug: procedure.slug },
      },
      { status: 201 }
    )
  } catch (err) {
    console.error("[procedures] POST error:", err)
    return NextResponse.json({ success: false as const, error: "Internal server error" }, { status: 500 })
  }
}
