import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { nowIso } from "./_utils/time";
import type { MutationCtx, QueryCtx } from "./_generated/server";

async function assertWorkspaceMember(
  ctx: QueryCtx | MutationCtx,
  userId: string,
  workspaceId: string,
) {
  const membership = await ctx.db
    .query("workspace_members")
    .withIndex("by_workspace_user", (q) =>
      q.eq("workspace_id", workspaceId).eq("user_id", userId),
    )
    .first();
  if (!membership) {
    throw new Error("无权访问该工作空间");
  }
  return membership;
}

export const getById = query({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    blockId: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    return await ctx.db
      .query("blocks")
      .withIndex("by_block_id", (q) => q.eq("id", args.blockId))
      .first();
  },
});

export const listByPage = query({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    pageId: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    return await ctx.db
      .query("blocks")
      .withIndex("by_page", (q) => q.eq("page_id", args.pageId))
      .collect();
  },
});

export const append = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    pageId: v.string(),
    blocks: v.array(
      v.object({
        id: v.string(),
        parentBlockId: v.union(v.string(), v.null()),
        type: v.string(),
        props: v.optional(v.any()),
        content: v.optional(v.any()),
        orderKey: v.string(),
      }),
    ),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const ts = nowIso();
    for (const block of args.blocks) {
      await ctx.db.insert("blocks", {
        id: block.id,
        workspace_id: args.workspaceId,
        page_id: args.pageId,
        parent_block_id: block.parentBlockId,
        type: block.type,
        props: block.props,
        content: block.content,
        child_block_ids: [],
        created_by: args.userId,
        created_at: ts,
        updated_at: ts,
      });
    }
    return { ok: true, count: args.blocks.length };
  },
});

export const insertAfter = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    pageId: v.string(),
    afterBlockId: v.string(),
    block: v.object({
      id: v.string(),
      parentBlockId: v.union(v.string(), v.null()),
      type: v.string(),
      props: v.optional(v.any()),
      content: v.optional(v.any()),
      orderKey: v.string(),
    }),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const ts = nowIso();
    await ctx.db.insert("blocks", {
      id: args.block.id,
      workspace_id: args.workspaceId,
      page_id: args.pageId,
      parent_block_id: args.block.parentBlockId,
      type: args.block.type,
      props: args.block.props,
      content: args.block.content,
      child_block_ids: [],
      created_by: args.userId,
      created_at: ts,
      updated_at: ts,
    });
    return { ok: true, blockId: args.block.id, afterBlockId: args.afterBlockId };
  },
});

export const replace = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    blockId: v.string(),
    type: v.string(),
    props: v.optional(v.any()),
    content: v.optional(v.any()),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const block = await ctx.db
      .query("blocks")
      .withIndex("by_block_id", (q) => q.eq("id", args.blockId))
      .first();
    if (!block) throw new Error("块不存在");
    await ctx.db.patch(block._id, {
      type: args.type,
      props: args.props,
      content: args.content,
      updated_at: nowIso(),
    });
    return { ok: true };
  },
});

export const remove = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    blockId: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const block = await ctx.db
      .query("blocks")
      .withIndex("by_block_id", (q) => q.eq("id", args.blockId))
      .first();
    if (!block) throw new Error("块不存在");
    await ctx.db.delete(block._id);
    return { ok: true };
  },
});
