import "server-only";

import type { ContentStatus, Prisma } from "@/generated/prisma/client";
import type { AuthorOption } from "@/features/authors/queries";
import type { CategoryOption } from "@/features/categories/queries";
import { getBlogDefaults } from "@/features/settings/queries";
import { db } from "@/lib/db";

import type { BlogEditorMeta, BlogFormValues } from "./schemas";
import {
  blogEditorInclude,
  emptyFormValues,
  parseSnapshot,
  toEditorMeta,
  toFormValues,
} from "./service";

export const BLOG_SORTS = {
  updated: { label: "Last updated", orderBy: { updatedAt: "desc" } },
  newest: { label: "Newest first", orderBy: { createdAt: "desc" } },
  oldest: { label: "Oldest first", orderBy: { createdAt: "asc" } },
  published: {
    label: "Publish date",
    orderBy: { publishedAt: { sort: "desc", nulls: "last" } },
  },
  title: { label: "Title A–Z", orderBy: { title: "asc" } },
} satisfies Record<string, { label: string; orderBy: Prisma.BlogOrderByWithRelationInput }>;

export type BlogSort = keyof typeof BLOG_SORTS;

export type BlogListFilters = {
  query?: string;
  status?: ContentStatus;
  categoryId?: string;
  authorId?: string;
  sort?: BlogSort;
  page?: number;
  pageSize?: number;
};

export async function listBlogs({
  query,
  status,
  categoryId,
  authorId,
  sort = "updated",
  page = 1,
  pageSize = 15,
}: BlogListFilters) {
  const q = query?.trim();
  const where: Prisma.BlogWhereInput = {
    ...(status && { status }),
    ...(categoryId && { categoryId: categoryId === "none" ? null : categoryId }),
    ...(authorId && { authorId: authorId === "none" ? null : authorId }),
    ...(q && {
      OR: [
        { title: { contains: q, mode: "insensitive" } },
        { slug: { contains: q, mode: "insensitive" } },
        { excerpt: { contains: q, mode: "insensitive" } },
      ],
    }),
  };

  const [items, total] = await Promise.all([
    db.blog.findMany({
      where,
      orderBy: [BLOG_SORTS[sort].orderBy, { id: "asc" }],
      skip: (page - 1) * pageSize,
      take: pageSize,
      select: {
        id: true,
        title: true,
        slug: true,
        status: true,
        featuredImage: true,
        featuredImageAlt: true,
        publishedAt: true,
        scheduledAt: true,
        updatedAt: true,
        autosavedAt: true,
        category: { select: { id: true, name: true } },
        author: { select: { id: true, name: true, profileImage: true } },
        updatedBy: { select: { name: true } },
      },
    }),
    db.blog.count({ where }),
  ]);

  return { items, total };
}

export type BlogListItem = Awaited<ReturnType<typeof listBlogs>>["items"][number];

export type BlogEditorData = {
  meta: BlogEditorMeta | null;
  /** What's saved (or live). The editor's baseline for unsaved-change tracking. */
  saved: BlogFormValues;
  /** Autosaved edits to a live article, loaded into the form on open. */
  pending: BlogFormValues | null;
};

export async function getBlogEditorData(id: string): Promise<BlogEditorData | null> {
  const blog = await db.blog.findUnique({ where: { id }, include: blogEditorInclude });
  if (!blog) return null;

  const saved = toFormValues(blog);
  return {
    meta: toEditorMeta(blog),
    saved,
    pending: blog.autosave ? parseSnapshot(blog.autosave, saved) : null,
  };
}

export async function getNewBlogEditorData(): Promise<BlogEditorData> {
  return {
    meta: null,
    saved: emptyFormValues(await getBlogDefaults()),
    pending: null,
  };
}

export async function getDashboardData() {
  const [statusCounts, categories, authors, recent, updated, scheduled] = await Promise.all([
    db.blog.groupBy({ by: ["status"], _count: { _all: true } }),
    db.category.count(),
    db.author.count(),
    db.blog.findMany({
      orderBy: { createdAt: "desc" },
      take: 5,
      select: dashboardSelect,
    }),
    db.blog.findMany({
      orderBy: { updatedAt: "desc" },
      take: 5,
      select: dashboardSelect,
    }),
    db.blog.findMany({
      where: { status: "SCHEDULED" },
      orderBy: { scheduledAt: "asc" },
      take: 5,
      select: dashboardSelect,
    }),
  ]);

  const count = (status: ContentStatus) =>
    statusCounts.find((row) => row.status === status)?._count._all ?? 0;

  return {
    stats: {
      total: statusCounts.reduce((sum, row) => sum + row._count._all, 0),
      published: count("PUBLISHED"),
      drafts: count("DRAFT"),
      scheduled: count("SCHEDULED"),
      categories,
      authors,
    },
    recent,
    updated,
    scheduled,
  };
}

const dashboardSelect = {
  id: true,
  title: true,
  slug: true,
  status: true,
  featuredImage: true,
  createdAt: true,
  updatedAt: true,
  publishedAt: true,
  scheduledAt: true,
  author: { select: { name: true } },
  category: { select: { name: true } },
  updatedBy: { select: { name: true } },
} satisfies Prisma.BlogSelect;

export type DashboardBlog = Prisma.BlogGetPayload<{ select: typeof dashboardSelect }>;

export async function listRevisions(blogId: string) {
  return db.blogRevision.findMany({
    where: { blogId },
    orderBy: { createdAt: "desc" },
    select: {
      id: true,
      title: true,
      slug: true,
      reason: true,
      createdAt: true,
      editedBy: { select: { name: true, avatarUrl: true } },
    },
  });
}

export async function getRevision(blogId: string, revisionId: string) {
  return db.blogRevision.findFirst({
    where: { id: revisionId, blogId },
    include: { editedBy: { select: { name: true } } },
  });
}

/** Existing tag names, most used first, for the editor's tag input. */
export async function getTagSuggestions() {
  const tags = await db.tag.findMany({
    select: { name: true },
    orderBy: { blogs: { _count: "desc" } },
    take: 300,
  });
  return tags.map((tag) => tag.name);
}

export type TaxonomyOptions = {
  categories: CategoryOption[];
  authors: AuthorOption[];
};
