import { builder } from "../builder";
import { ArticleType, ArticleListType, AdminArticleListType } from "../types/index";
import { articles, users } from "../../db/schema/index";
import { eq, and, lte, desc, asc, ilike, or, count, arrayContains, isNotNull, type SQL } from "drizzle-orm";
import { requireAdmin } from "../../lib/acl";
import type { DB } from "../../db/connection";

async function enrichArticle(db: DB, article: typeof articles.$inferSelect) {
  let author = null;
  if (article.author) {
    const authorUser = await db
      .select({
        id: users.id,
        artist_name: users.artist_name,
        slug: users.slug,
        avatar: users.avatar,
        description: users.description,
      })
      .from(users)
      .where(eq(users.id, article.author))
      .limit(1);
    author = authorUser[0] || null;
  }
  return { ...article, author };
}

builder.queryField("articles", (t) =>
  t.field({
    type: ArticleListType,
    args: {
      featured: t.arg.boolean(),
      limit: t.arg.int(),
      search: t.arg.string(),
      category: t.arg.string(),
      offset: t.arg.int(),
      sortBy: t.arg.string(),
      tag: t.arg.string(),
    },
    resolve: async (_root, args, ctx) => {
      const pageSize = args.limit ?? 24;
      const offset = args.offset ?? 0;

      const conditions: SQL<unknown>[] = [lte(articles.publish_date, new Date())];
      if (args.featured !== null && args.featured !== undefined) {
        conditions.push(eq(articles.featured, args.featured));
      }
      if (args.category) conditions.push(eq(articles.category, args.category));
      if (args.tag) conditions.push(arrayContains(articles.tags, [args.tag]));
      if (args.search) {
        conditions.push(
          or(
            ilike(articles.title, `%${args.search}%`),
            ilike(articles.excerpt, `%${args.search}%`),
          ) as SQL<unknown>,
        );
      }

      const where = and(...conditions);
      const baseQuery = ctx.db.select().from(articles).where(where);
      const ordered =
        args.sortBy === "name"
          ? baseQuery.orderBy(asc(articles.title))
          : args.sortBy === "featured"
            ? baseQuery.orderBy(desc(articles.featured), desc(articles.publish_date))
            : baseQuery.orderBy(desc(articles.publish_date));

      const [articleList, totalRows] = await Promise.all([
        ordered.limit(pageSize).offset(offset),
        ctx.db.select({ total: count() }).from(articles).where(where),
      ]);
      const items = await Promise.all(articleList.map((article) => enrichArticle(ctx.db, article)));
      return { items, total: totalRows[0]?.total ?? 0 };
    },
  }),
);

builder.queryField("articleCategories", (t) =>
  t.field({
    type: ["String"],
    resolve: async (_root, _args, ctx) => {
      const rows = await ctx.db
        .selectDistinct({ category: articles.category })
        .from(articles)
        .where(and(lte(articles.publish_date, new Date()), isNotNull(articles.category)))
        .orderBy(asc(articles.category));
      return rows.map((r) => r.category!);
    },
  }),
);

builder.queryField("article", (t) =>
  t.field({
    type: ArticleType,
    nullable: true,
    args: {
      slug: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      const article = await ctx.db
        .select()
        .from(articles)
        .where(and(eq(articles.slug, args.slug), lte(articles.publish_date, new Date())))
        .limit(1);

      if (!article[0]) return null;
      return enrichArticle(ctx.db, article[0]);
    },
  }),
);

builder.queryField("adminGetArticle", (t) =>
  t.field({
    type: ArticleType,
    nullable: true,
    args: {
      id: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      requireAdmin(ctx);
      const article = await ctx.db.select().from(articles).where(eq(articles.id, args.id)).limit(1);
      if (!article[0]) return null;
      return enrichArticle(ctx.db, article[0]);
    },
  }),
);

// ─── Admin queries & mutations ────────────────────────────────────────────────

builder.queryField("adminListArticles", (t) =>
  t.field({
    type: AdminArticleListType,
    args: {
      search: t.arg.string(),
      category: t.arg.string(),
      featured: t.arg.boolean(),
      limit: t.arg.int(),
      offset: t.arg.int(),
    },
    resolve: async (_root, args, ctx) => {
      requireAdmin(ctx);
      const limit = args.limit ?? 50;
      const offset = args.offset ?? 0;

      const conditions: SQL<unknown>[] = [];
      if (args.search) {
        conditions.push(
          or(
            ilike(articles.title, `%${args.search}%`),
            ilike(articles.excerpt, `%${args.search}%`),
          ) as SQL<unknown>,
        );
      }
      if (args.category) conditions.push(eq(articles.category, args.category));
      if (args.featured !== null && args.featured !== undefined)
        conditions.push(eq(articles.featured, args.featured));

      const where = conditions.length > 0 ? and(...conditions) : undefined;
      const [articleList, totalRows] = await Promise.all([
        ctx.db
          .select()
          .from(articles)
          .where(where)
          .orderBy(desc(articles.publish_date))
          .limit(limit)
          .offset(offset),
        ctx.db.select({ total: count() }).from(articles).where(where),
      ]);
      const items = await Promise.all(articleList.map((article) => enrichArticle(ctx.db, article)));
      return { items, total: totalRows[0]?.total ?? 0 };
    },
  }),
);

builder.mutationField("createArticle", (t) =>
  t.field({
    type: ArticleType,
    args: {
      title: t.arg.string({ required: true }),
      slug: t.arg.string({ required: true }),
      excerpt: t.arg.string(),
      content: t.arg.string(),
      imageId: t.arg.string(),
      tags: t.arg.stringList(),
      category: t.arg.string(),
      featured: t.arg.boolean(),
      publishDate: t.arg.string(),
    },
    resolve: async (_root, args, ctx) => {
      requireAdmin(ctx);
      const inserted = await ctx.db
        .insert(articles)
        .values({
          title: args.title,
          slug: args.slug,
          excerpt: args.excerpt || null,
          content: args.content || null,
          image: args.imageId || null,
          tags: args.tags || [],
          category: args.category || null,
          featured: args.featured ?? false,
          publish_date: args.publishDate ? new Date(args.publishDate) : new Date(),
          author: ctx.currentUser!.id,
        })
        .returning();
      return enrichArticle(ctx.db, inserted[0]);
    },
  }),
);

builder.mutationField("updateArticle", (t) =>
  t.field({
    type: ArticleType,
    nullable: true,
    args: {
      id: t.arg.string({ required: true }),
      title: t.arg.string(),
      slug: t.arg.string(),
      excerpt: t.arg.string(),
      content: t.arg.string(),
      imageId: t.arg.string(),
      authorId: t.arg.string(),
      tags: t.arg.stringList(),
      category: t.arg.string(),
      featured: t.arg.boolean(),
      publishDate: t.arg.string(),
    },
    resolve: async (_root, args, ctx) => {
      requireAdmin(ctx);
      const updates: Record<string, unknown> = { date_updated: new Date() };
      if (args.title !== undefined && args.title !== null) updates.title = args.title;
      if (args.slug !== undefined && args.slug !== null) updates.slug = args.slug;
      if (args.excerpt !== undefined) updates.excerpt = args.excerpt;
      if (args.content !== undefined) updates.content = args.content;
      if (args.imageId !== undefined) updates.image = args.imageId;
      if (args.authorId !== undefined) updates.author = args.authorId;
      if (args.tags !== undefined && args.tags !== null) updates.tags = args.tags;
      if (args.category !== undefined) updates.category = args.category;
      if (args.featured !== undefined && args.featured !== null) updates.featured = args.featured;
      if (args.publishDate !== undefined && args.publishDate !== null)
        updates.publish_date = new Date(args.publishDate);

      const updated = await ctx.db
        .update(articles)
        .set(updates as Partial<typeof articles.$inferInsert>)
        .where(eq(articles.id, args.id))
        .returning();
      if (!updated[0]) return null;
      return enrichArticle(ctx.db, updated[0]);
    },
  }),
);

builder.mutationField("deleteArticle", (t) =>
  t.field({
    type: "Boolean",
    args: {
      id: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      requireAdmin(ctx);
      await ctx.db.delete(articles).where(eq(articles.id, args.id));
      return true;
    },
  }),
);
