/**
 * Finzone v2 article repository.
 * Centralizes create/update/publish flows, slug uniqueness, SEO + JSON-LD computation.
 */
import { Article, Prisma } from "@prisma/client";
import { prisma } from "./db";
import { autoExcerpt, slugify } from "./slug-vi";
import { buildJsonLd, readabilityScore } from "./seo-engine";
import { analyzeArticle } from "./seo/analyzer";

export type ArticleInput = {
  title: string;
  slug?: string | null;
  excerpt?: string | null;
  content: string;
  coverImage?: string | null;
  status?: "draft" | "published" | "scheduled" | "archived";
  publishedAt?: Date | null;
  scheduledFor?: Date | null;
  metaTitle?: string | null;
  metaDesc?: string | null;
  metaKeywords?: string | null;
  focusKeyword?: string | null;
  ogImage?: string | null;
  canonicalUrl?: string | null;
  noIndex?: boolean;
  categoryId?: string | null;
  categorySlug?: string | null;
  tags?: string[]; // tag names (will upsert by slug)
  authorId: string;
  // Promotion
  featured?: boolean;
  pinned?: boolean;
  pinOrder?: number;
};

async function uniqueSlug(base: string, ignoreId?: string): Promise<string> {
  const seed = slugify(base) || `bai-viet-${Date.now()}`;
  let candidate = seed;
  let n = 1;
  while (true) {
    const exists = await prisma.article.findFirst({
      where: { slug: candidate, NOT: ignoreId ? { id: ignoreId } : undefined },
      select: { id: true },
    });
    if (!exists) return candidate;
    n += 1;
    candidate = `${seed}-${n}`;
    if (n > 50) return `${seed}-${Date.now()}`;
  }
}

async function resolveCategoryId(input: ArticleInput): Promise<string | null> {
  if (input.categoryId) return input.categoryId;
  if (input.categorySlug) {
    const c = await prisma.category.findUnique({
      where: { slug: input.categorySlug },
      select: { id: true },
    });
    return c?.id ?? null;
  }
  return null;
}

async function resolveTagIds(names: string[]): Promise<string[]> {
  if (!names.length) return [];
  const out: string[] = [];
  for (const raw of names) {
    const name = raw.trim();
    if (!name) continue;
    const slug = slugify(name);
    if (!slug) continue;
    const t = await prisma.tag.upsert({
      where: { slug },
      create: { slug, name },
      update: {},
      select: { id: true },
    });
    out.push(t.id);
  }
  return out;
}

function siteName(): string {
  return process.env.SITE_NAME ?? "Finzone v2";
}

function siteUrl(): string {
  return process.env.NEXT_PUBLIC_SITE_URL ?? "https://v2.finzone.io.vn";
}

function computeSeoFields(input: ArticleInput, finalSlug: string) {
  const report = analyzeArticle({
    title: input.title,
    metaTitle: input.metaTitle ?? null,
    slug: finalSlug,
    metaDesc: input.metaDesc ?? null,
    focusKeyword: input.focusKeyword ?? null,
    metaKeywords: input.metaKeywords ?? null,
    content: input.content,
    coverImage: input.coverImage ?? null,
  });
  const readability = readabilityScore(input.content);
  // Persist analyzer report shape so SEO Health page + sidebar share single source of truth.
  return {
    seoScore: report.score,
    seoChecks: {
      grade: report.grade,
      checks: report.checks,
      stats: report.stats,
    },
    readabilityScore: readability,
  };
}

function computeJsonLd(args: {
  title: string;
  metaDesc?: string | null;
  excerpt?: string | null;
  content: string;
  slug: string;
  coverImage?: string | null;
  authorName: string;
  publishedAt: Date | null;
  updatedAt: Date;
}): Prisma.JsonObject {
  const description =
    args.metaDesc?.trim() ||
    args.excerpt?.trim() ||
    autoExcerpt(args.content);
  return buildJsonLd({
    title: args.title,
    description,
    url: `${siteUrl()}/bai-viet/${args.slug}`,
    image: args.coverImage,
    authorName: args.authorName,
    publishedAt: args.publishedAt,
    updatedAt: args.updatedAt,
    siteName: siteName(),
  }) as Prisma.JsonObject;
}

export async function createArticle(input: ArticleInput): Promise<Article> {
  const finalSlug = await uniqueSlug(input.slug || input.title);
  const categoryId = await resolveCategoryId(input);
  const tagIds = await resolveTagIds(input.tags ?? []);
  const status = input.status ?? "draft";
  const publishedAt =
    status === "published"
      ? input.publishedAt ?? new Date()
      : input.publishedAt ?? null;
  const scheduledFor = status === "scheduled" ? input.scheduledFor ?? null : null;

  const author = await prisma.user.findUnique({
    where: { id: input.authorId },
    select: { name: true },
  });
  if (!author) throw new Error(`author ${input.authorId} not found`);

  const seoFields = computeSeoFields(input, finalSlug);
  const now = new Date();
  const jsonLd = computeJsonLd({
    title: input.title,
    metaDesc: input.metaDesc,
    excerpt: input.excerpt,
    content: input.content,
    slug: finalSlug,
    coverImage: input.coverImage,
    authorName: author.name,
    publishedAt,
    updatedAt: now,
  });

  return prisma.article.create({
    data: {
      slug: finalSlug,
      title: input.title,
      excerpt: input.excerpt ?? autoExcerpt(input.content),
      content: input.content,
      coverImage: input.coverImage ?? null,
      status,
      publishedAt,
      scheduledFor,
      metaTitle: input.metaTitle ?? null,
      metaDesc: input.metaDesc ?? null,
      metaKeywords: input.metaKeywords ?? null,
      focusKeyword: input.focusKeyword ?? null,
      ogImage: input.ogImage ?? null,
      canonicalUrl: input.canonicalUrl ?? null,
      noIndex: input.noIndex ?? false,
      jsonLd,
      seoScore: seoFields.seoScore,
      seoChecks: seoFields.seoChecks as never,
      readabilityScore: seoFields.readabilityScore,
      categoryId,
      authorId: input.authorId,
      featured: input.featured ?? false,
      pinned: input.pinned ?? false,
      pinOrder: input.pinOrder ?? 0,
      tags: tagIds.length
        ? { create: tagIds.map((tagId) => ({ tagId })) }
        : undefined,
    },
  });
}

export type ArticleUpdate = Partial<ArticleInput> & { id: string };

export async function updateArticle(input: ArticleUpdate): Promise<Article> {
  const existing = await prisma.article.findUnique({
    where: { id: input.id },
    include: { author: { select: { name: true } } },
  });
  if (!existing) throw new Error(`article ${input.id} not found`);

  const merged = {
    title: input.title ?? existing.title,
    content: input.content ?? existing.content,
    excerpt: input.excerpt ?? existing.excerpt,
    metaTitle: input.metaTitle ?? existing.metaTitle,
    metaDesc: input.metaDesc ?? existing.metaDesc,
    focusKeyword: input.focusKeyword ?? existing.focusKeyword,
    coverImage: input.coverImage ?? existing.coverImage,
  };

  let finalSlug = existing.slug;
  if (input.slug && input.slug !== existing.slug) {
    finalSlug = await uniqueSlug(input.slug, existing.id);
  }

  const categoryId =
    input.categoryId !== undefined || input.categorySlug !== undefined
      ? await resolveCategoryId({
          ...input,
          authorId: existing.authorId,
          title: merged.title,
          content: merged.content,
        })
      : existing.categoryId;

  const status = input.status ?? existing.status;
  const wasUnpublished = existing.status !== "published";
  const publishedAt =
    status === "published"
      ? input.publishedAt ?? existing.publishedAt ?? (wasUnpublished ? new Date() : existing.publishedAt)
      : existing.publishedAt;

  const seoFields = computeSeoFields(
    {
      title: merged.title,
      content: merged.content,
      metaTitle: merged.metaTitle,
      metaDesc: merged.metaDesc,
      focusKeyword: merged.focusKeyword,
      coverImage: merged.coverImage,
      authorId: existing.authorId,
    },
    finalSlug,
  );

  const now = new Date();
  const jsonLd = computeJsonLd({
    title: merged.title,
    metaDesc: merged.metaDesc,
    excerpt: merged.excerpt,
    content: merged.content,
    slug: finalSlug,
    coverImage: merged.coverImage,
    authorName: existing.author.name,
    publishedAt,
    updatedAt: now,
  });

  // Optional tag replacement
  let tagOps: Prisma.ArticleUpdateInput["tags"] = undefined;
  if (input.tags) {
    const tagIds = await resolveTagIds(input.tags);
    tagOps = {
      deleteMany: {},
      create: tagIds.map((tagId) => ({ tagId })),
    };
  }

  return prisma.article.update({
    where: { id: existing.id },
    data: {
      slug: finalSlug,
      title: merged.title,
      content: merged.content,
      excerpt: merged.excerpt,
      coverImage: merged.coverImage,
      metaTitle: merged.metaTitle,
      metaDesc: merged.metaDesc,
      metaKeywords: input.metaKeywords ?? existing.metaKeywords,
      focusKeyword: merged.focusKeyword,
      ogImage: input.ogImage ?? existing.ogImage,
      canonicalUrl: input.canonicalUrl ?? existing.canonicalUrl,
      noIndex: input.noIndex ?? existing.noIndex,
      status,
      publishedAt,
      scheduledFor: input.scheduledFor ?? existing.scheduledFor,
      categoryId,
      jsonLd,
      seoScore: seoFields.seoScore,
      seoChecks: seoFields.seoChecks as never,
      readabilityScore: seoFields.readabilityScore,
      featured: input.featured ?? existing.featured,
      pinned: input.pinned ?? existing.pinned,
      pinOrder: input.pinOrder ?? existing.pinOrder,
      tags: tagOps,
    },
  });
}

export async function publishArticle(id: string, when?: Date): Promise<Article> {
  const a = await prisma.article.findUnique({ where: { id } });
  if (!a) throw new Error(`article ${id} not found`);
  return updateArticle({
    id,
    status: "published",
    publishedAt: when ?? a.publishedAt ?? new Date(),
    authorId: a.authorId,
    title: a.title,
    content: a.content,
  });
}

export async function unpublishArticle(id: string): Promise<Article> {
  return prisma.article.update({
    where: { id },
    data: { status: "draft" },
  });
}

export async function deleteArticle(id: string): Promise<void> {
  await prisma.article.delete({ where: { id } });
}
