﻿import { randomUUID } from "crypto"
import { NextRequest, NextResponse } from "next/server"
import { prisma } from "@/lib/prisma"
import {
  buildContentAudit,
  contentAuditCreateData,
  contentAuditArticleInclude,
  requireContentAuditActor,
} from "@/lib/content-audit"
import {
  extractMediaKeysFromHtml,
} from "@/lib/r2-storage"
import { sanitizeArticleHtml } from "@/lib/sanitize-article-html"
import { parseArticleTagsFromBody } from "@/lib/article-tags"
import {
  ARTICLE_LOCALES,
  buildArticleTranslations,
  parseArticleLocale,
  parseArticlePayloadTranslations,
  parseArticleStoredJson,
  parseArticleTagsFromDb,
  resolveArticleDisplayAuthor,
  resolveArticleDisplayTitle,
  resolveLocalizedArticle,
  serializeArticleFaqForDb,
  serializeArticleLongTextJsonField,
  serializeArticleOverviewForDb,
  serializeArticleTagsForDb,
} from "@/lib/article-db"
import type { Article_status } from "@prisma/client"
import { Prisma } from "@prisma/client"
import { validateUrlSlug } from "@/lib/slug-validation"
import { getPersistedMediaValidationError } from "@/lib/media-manager/validate-persisted-payload"
import { articleMediaBaseForSlug } from "@/lib/article-media"
import { reconcileArticleMediaAfterSave } from "@/lib/media-manager/slug-media-reconcile"

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|createdbyid|updatedbyid/i.test(message)
}

function toJsonArray(value: unknown): Prisma.InputJsonValue {
  if (Array.isArray(value)) return value as Prisma.InputJsonValue
  return []
}

function parseOverviewFromBody(value: unknown): { data: Prisma.InputJsonValue; error?: string } {
  if (value === null || value === undefined) {
    return { data: {} }
  }

  if (Array.isArray(value)) {
    const pairs = value.filter(
      (item): item is { label: string; value: string } =>
        !!item &&
        typeof item === "object" &&
        "label" in item &&
        "value" in item &&
        typeof (item as { label: unknown }).label === "string" &&
        typeof (item as { value: unknown }).value === "string"
    )
    if (pairs.length === 0) return { data: {} }
    return {
      data: {
        columns: ["Label", "Value", "Description", "Notes"],
        rows: pairs.map((p) => [p.label, p.value, "", ""]),
      } as Prisma.InputJsonValue,
    }
  }

  if (!value || typeof value !== "object") {
    return { data: {}, error: "Overview format is invalid" }
  }

  const source = value as { columns?: unknown; rows?: unknown }

  if (source.columns === undefined && source.rows === undefined) {
    return { data: {} }
  }

  // Accept flexible columns/rows. Normalize to strings and preserve structure.
  const cols = Array.isArray(source.columns) ? source.columns.map((c) => (c === null || c === undefined ? "" : String(c))) : []
  const rowsRaw = Array.isArray(source.rows) ? source.rows : []
  const normalizedRows: string[][] = []
  for (const row of rowsRaw) {
    if (!Array.isArray(row)) continue
    normalizedRows.push(row.map((cell) => (cell === null || cell === undefined ? "" : String(cell))))
  }

  return {
    data: {
      columns: cols,
      rows: normalizedRows,
    } as Prisma.InputJsonValue,
  }
}

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 collectInlineImagePublicIdsFromContents(contents: Array<string | null | undefined>): Set<string> {
  const ids = new Set<string>()
  for (const content of contents) {
    const extracted = extractMediaKeysFromHtml(content, { prefix: "articles" })
    for (const id of extracted) ids.add(id)
  }
  return ids
}

export async function GET(req: NextRequest) {
  try {
    const locale = parseArticleLocale(req.nextUrl.searchParams.get("locale"))
    let rows: Array<{
      id: string
      slug: string
      title: string
      subTitle: string | null
      author: string
      content: string
      faqHeading: string | null
      overview: Prisma.JsonValue
      faq: Prisma.JsonValue
      tags: string
      categoryId: string
      readingTime: string
      publishDate: Date
      status: Article_status
      image: string
      isFeatured: boolean
      Category: { id: string; name: string; color: string }
      createdAt: Date
      updatedAt: Date
      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.article.findMany({
        orderBy: { updatedAt: "desc" },
        include: {
          Category: { select: { id: true, name: true, color: true } },
          ...contentAuditArticleInclude,
        },
      })
    } catch (err) {
      if (!isSchemaDriftError(err)) {
        console.warn("[articles] primary query failed, trying compatibility fallback:", err)
      }
      rows = await prisma.article.findMany({
        orderBy: { updatedAt: "desc" },
        include: {
          Category: { select: { id: true, name: true, color: true } },
        },
      })
    }

    const articleIds = rows.map((row) => row.id)
    const translationRows = articleIds.length
      ? await prisma.articleTranslation.findMany({
          where: { articleId: { in: articleIds } },
          select: { articleId: true, locale: true, title: true, subTitle: true, author: true, content: true, overview: true, faq: true, tags: true },
        })
      : []
    const byArticleId = new Map<string, typeof translationRows>()
    for (const tr of translationRows) {
      const bucket = byArticleId.get(tr.articleId)
      if (bucket) bucket.push(tr)
      else byArticleId.set(tr.articleId, [tr])
    }

    function normalizeRows(rows: typeof translationRows) {
      return rows.map((r) => ({
        locale: r.locale,
        title: r.title ?? null,
        subTitle: r.subTitle ?? null,
        author: r.author ?? null,
        content: r.content ?? null,
        overview: r.overview,
        faq: r.faq,
        tags: r.tags,
      })) as unknown as any
    }

    return NextResponse.json({
      success: true as const,
      articles: rows.map((a) => {
        const translations = buildArticleTranslations(
          {
            title: a.title,
            subTitle: a.subTitle,
            author: a.author,
            content: a.content,
            faqHeading: a.faqHeading ?? "",
            overview: a.overview,
            faq: a.faq,
            tags: a.tags,
          },
          normalizeRows(byArticleId.get(a.id) ?? [])
        )
        const localized = resolveLocalizedArticle(translations, locale)
        const displayTitle = resolveArticleDisplayTitle(translations)
        const displayAuthor = resolveArticleDisplayAuthor(translations)
        return {
          id: a.id,
          slug: a.slug,
          title: displayTitle || localized.title || a.title,
          subTitle: localized.subTitle ?? "",
          category: a.Category.name,
          categoryId: a.categoryId,
          categoryColor: a.Category.color,
          author: displayAuthor || localized.author || a.author,
          readingTime: a.readingTime,
          publishDate: a.publishDate.toISOString(),
          status: a.status === "published" ? ("published" as const) : ("draft" as const),
          image: a.image,
          isFeatured: a.isFeatured,
          tags: localized.tags.length > 0 ? localized.tags : parseArticleTagsFromDb(a.tags),
          translations,
          audit: buildContentAudit(a),
        }
      }),
    }, { headers: NO_CACHE_HEADERS })
  } catch (err) {
    console.error("[articles] GET error:", err)
    return NextResponse.json(
      { success: false as const, error: "Failed to load articles" },
      { status: 500 }
    )
  }
}

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

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

    const payload = body as Record<string, unknown>
    const translations = parseArticlePayloadTranslations(payload)

    const status: Article_status =
      body.status === "draft" ? "draft" : "published"

    const categoryId = typeof body.categoryId === "string" ? body.categoryId.trim() : ""
    const image = typeof body.image === "string" ? body.image.trim() : ""
    const imagePublicId = typeof body.imagePublicId === "string" ? body.imagePublicId.trim() : ""
    const title = translations.en.title
    const subTitle = translations.en.subTitle
    const author = translations.en.author || (typeof body.author === "string" ? body.author.trim() : "")
    const readingTimeRaw = body.readingTime
    const readingTime =
      readingTimeRaw === "" || readingTimeRaw === undefined || readingTimeRaw === null
        ? ""
        : String(readingTimeRaw).trim()
    const publishDateRaw = body.publishDate
    const content = translations.en.content || (typeof body.content === "string" ? body.content : "")
    const overviewResult = parseOverviewFromBody(body.overview)
    if (overviewResult.error) {
      return fail(overviewResult.error)
    }
    const overview = (translations.en.overview ?? overviewResult.data) as Prisma.InputJsonValue
    const faq = (translations.en.faq ?? toJsonArray(body.faq)) as Prisma.InputJsonValue
    const tags = parseArticleTagsFromBody(body.tags)
    const isFeatured = parseIsFeatured(body.isFeatured)

    if (!categoryId) {
      return fail("Category is required")
    }

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

    if (status === "published") {
      if (!image) {
        return fail("Article image is required")
      }
      if (!imagePublicId) {
        return fail("Image public id is required")
      }
    }

    let publishDate: Date
    if (typeof publishDateRaw === "string" && publishDateRaw.trim() !== "") {
      const d = new Date(publishDateRaw)
      if (Number.isNaN(d.getTime())) {
        return fail("Invalid publish date")
      }
      publishDate = d
    } else {
      publishDate = new Date()
    }

    // Require client-provided slug (do not auto-generate)
    const requestedSlug = typeof body.slug === "string" ? body.slug.trim() : ""
    const slugValidation = validateUrlSlug(requestedSlug)
    if (!slugValidation.valid) {
      return fail(slugValidation.error ?? "Invalid slug")
    }
    // Ensure uniqueness
    const existingSlug = await prisma.article.findUnique({ where: { slug: requestedSlug } })
    if (existingSlug) {
      return NextResponse.json({ success: false as const, error: "Slug already in use" }, { status: 409 })
    }
    const slug = requestedSlug
    const safeContent = sanitizeArticleHtml(content)
    const safeImage = image || ""
    const safePublicId = imagePublicId || ""

    const nextContentByLocale: Record<string, string | null> = {
      en: safeContent,
      ar: null,
      tr: null,
    }
    const contentImageFolder = articleMediaBaseForSlug(slug)
    const now = new Date()
    const article = await prisma.article.create({
      data: {
        id: randomUUID(),
        title,
        subTitle,
        slug,
        faqHeading: typeof body.faqHeading === "string" ? body.faqHeading : null,
        categoryId,
        image: safeImage,
        imagePublicId: safePublicId,
        author,
        readingTime: readingTime || "0",
        publishDate,
        content: safeContent,
        contentImageFolder,
        conversionCard: serializeArticleLongTextJsonField(translations.en.conversionCard),
        trustCard: serializeArticleLongTextJsonField(translations.en.trustCard),
        overview: serializeArticleOverviewForDb(overview),
        faq: serializeArticleFaqForDb(faq),
        tags: serializeArticleTagsForDb(tags),
        isFeatured,
        status,
        ...contentAuditCreateData(auth.user.id),
      },
      include: {
        Category: { select: { id: true, name: true, slug: true } },
        ...contentAuditArticleInclude,
      },
    })

    const translationCreateRows = ARTICLE_LOCALES
      .map((localeCode) => ({ localeCode, data: translations[localeCode] }))
      .filter(
        ({ localeCode, data }) =>
          localeCode === "en" || data.title || data.subTitle || data.author || data.content || data.tags.length > 0
      )
      .map(({ localeCode, data }) => {
        const safeTranslationContent = data.content ? sanitizeArticleHtml(data.content) : null
        nextContentByLocale[localeCode] = safeTranslationContent
        return {
        id: randomUUID(),
        updatedAt: now,
        articleId: article.id,
        locale: localeCode,
        title: data.title || null,
        subTitle: data.subTitle,
        author: data.author || null,
        content: safeTranslationContent,
        faqHeading: data.faqHeading || null,
        overview: serializeArticleOverviewForDb(data.overview),
        faq: serializeArticleFaqForDb(data.faq),
        conversionCard: serializeArticleLongTextJsonField(data.conversionCard),
        trustCard: serializeArticleLongTextJsonField(data.trustCard),
        tags: serializeArticleTagsForDb(data.tags),
      }})
    if (translationCreateRows.length > 0) {
      await prisma.articleTranslation.createMany({ data: translationCreateRows })
    }
    const savedTranslations = await prisma.articleTranslation.findMany({
      where: { articleId: article.id },
      select: { locale: true, title: true, subTitle: true, author: true, content: true, faqHeading: true, overview: true, faq: true, tags: true, conversionCard: true, trustCard: true },
    })
    const normalizedSaved = savedTranslations.map((r) => ({
      locale: r.locale,
      title: r.title ?? null,
      subTitle: r.subTitle ?? null,
      author: r.author ?? null,
      content: r.content ?? null,
      faqHeading: r.faqHeading ?? null,
      overview: r.overview,
      faq: r.faq,
      conversionCard: r.conversionCard,
      trustCard: r.trustCard,
      tags: r.tags,
    })) as unknown as any
    const responseTranslations = buildArticleTranslations(
      {
        title: article.title,
        subTitle: article.subTitle,
        author: article.author,
        content: article.content,
        faqHeading: article.faqHeading ?? "",
        overview: article.overview,
        faq: article.faq,
        tags: article.tags,
        conversionCard: article.conversionCard,
        trustCard: article.trustCard,
      },
      normalizedSaved
    )
    await reconcileArticleMediaAfterSave({
      slugChanged: false,
      existingSlug: slug,
      existingContentImageFolder: contentImageFolder,
      newSlug: slug,
      previousMediaKeys: [],
      savedPayload: {
        image: safeImage,
        imagePublicId: safePublicId,
        content: safeContent,
        translations: responseTranslations,
        overview: parseArticleStoredJson(article.overview, {}),
        faq: parseArticleStoredJson(article.faq, []),
        conversionCard: responseTranslations.en.conversionCard,
        trustCard: responseTranslations.en.trustCard,
      },
    })

    return NextResponse.json(
      {
        success: true as const,
        article: {
          id: article.id,
          title: responseTranslations.en.title || article.title,
          subTitle: responseTranslations.en.subTitle,
          slug: article.slug,
          categoryId: article.categoryId,
          category: article.Category,
          image: article.image,
          imagePublicId: article.imagePublicId,
          contentImageFolder: article.contentImageFolder,
          author: responseTranslations.en.author || article.author,
          readingTime: article.readingTime,
          publishDate: article.publishDate.toISOString(),
          content: article.content,
          overview: parseArticleStoredJson(article.overview, {}),
          faq: parseArticleStoredJson(article.faq, []),
          conversionCard: parseArticleStoredJson(article.conversionCard, null),
          trustCard: parseArticleStoredJson(article.trustCard, null),
          tags:
            responseTranslations.en.tags.length > 0
              ? responseTranslations.en.tags
              : parseArticleTagsFromDb(article.tags),
          isFeatured: article.isFeatured,
          status: article.status,
          translations: responseTranslations,
          audit: buildContentAudit(article),
        },
      },
      { status: 201 }
    )
  } catch (err) {
    console.error("[articles] POST error:", err)
    return NextResponse.json(
      { success: false as const, error: "Internal server error" },
      { status: 500 }
    )
  }
}
