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 listWorkspacesByUser = query({
  args: { userId: v.string() },
  handler: async (ctx, args) => {
    const memberships = await ctx.db
      .query("workspace_members")
      .withIndex("by_user_id", (q) => q.eq("user_id", args.userId))
      .collect();

    const workspaceIds = Array.from(new Set(memberships.map((item) => item.workspace_id)));
    const result = [];
    for (const workspaceId of workspaceIds) {
      const workspace = await ctx.db
        .query("workspaces")
        .withIndex("by_workspace_id", (q) => q.eq("id", workspaceId))
        .first();
      if (!workspace) continue;
      const memberCount = await ctx.db
        .query("workspace_members")
        .withIndex("by_workspace_id", (q) => q.eq("workspace_id", workspaceId))
        .collect();
      result.push({
        id: workspace.id,
        name: workspace.name,
        type: workspace.type,
        iconUrl: workspace.icon_url ?? null,
        memberCount: memberCount.length,
        isDefault: memberships.some(
          (item) => item.workspace_id === workspaceId && item.is_default,
        ),
      });
    }

    return result;
  },
});

export const createPage = mutation({
  args: {
    userId: v.string(),
    id: v.string(),
    workspaceId: v.string(),
    parentId: v.union(v.string(), v.null()),
    title: v.string(),
    accessScope: v.union(
      v.literal("private"),
      v.literal("shared"),
      v.literal("public"),
    ),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const ts = nowIso();
    await ctx.db.insert("pages", {
      id: args.id,
      workspace_id: args.workspaceId,
      parent_id: args.parentId,
      title: args.title,
      access_scope: args.accessScope,
      block_ids: [],
      child_page_ids: [],
      asset_ids: [],
      created_by: args.userId,
      created_at: ts,
      updated_at: ts,
      deleted_at: null,
    });
    return {
      id: args.id,
      workspaceId: args.workspaceId,
      parentId: args.parentId,
      title: args.title,
      accessScope: args.accessScope,
      createdBy: args.userId,
      createdAt: ts,
      updatedAt: ts,
      archivedAt: null,
      sortOrder: null,
    };
  },
});

export const renamePage = mutation({
  args: {
    userId: v.string(),
    id: v.string(),
    title: v.string(),
  },
  handler: async (ctx, args) => {
    const page = await ctx.db
      .query("pages")
      .withIndex("by_page_id", (q) => q.eq("id", args.id))
      .first();
    if (!page) throw new Error("页面不存在");
    await assertWorkspaceMember(ctx, args.userId, page.workspace_id);
    const ts = nowIso();
    await ctx.db.patch(page._id, {
      title: args.title,
      updated_at: ts,
    });
    return {
      id: args.id,
      title: args.title,
      updatedAt: ts,
    };
  },
});

export const movePage = mutation({
  args: {
    userId: v.string(),
    id: v.string(),
    parentId: v.union(v.string(), v.null()),
  },
  handler: async (ctx, args) => {
    const page = await ctx.db
      .query("pages")
      .withIndex("by_page_id", (q) => q.eq("id", args.id))
      .first();
    if (!page) throw new Error("页面不存在");
    await assertWorkspaceMember(ctx, args.userId, page.workspace_id);
    const ts = nowIso();
    await ctx.db.patch(page._id, {
      parent_id: args.parentId,
      updated_at: ts,
    });
    return {
      id: args.id,
      parentId: args.parentId,
      sortOrder: null,
      updatedAt: ts,
    };
  },
});

export const archivePage = mutation({
  args: {
    userId: v.string(),
    id: v.string(),
  },
  handler: async (ctx, args) => {
    const page = await ctx.db
      .query("pages")
      .withIndex("by_page_id", (q) => q.eq("id", args.id))
      .first();
    if (!page) throw new Error("页面不存在");
    await assertWorkspaceMember(ctx, args.userId, page.workspace_id);
    const ts = nowIso();
    await ctx.db.patch(page._id, {
      deleted_at: ts,
      updated_at: ts,
    });
    return {
      id: args.id,
      archivedAt: ts,
      updatedAt: ts,
    };
  },
});

export const listPagesByWorkspace = query({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    return await ctx.db
      .query("pages")
      .withIndex("by_workspace", (q) => q.eq("workspace_id", args.workspaceId))
      .collect();
  },
});

export const appendBlocks = 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 {
      count: args.blocks.length,
      pageId: args.pageId,
      blockIds: args.blocks.map((item) => item.id),
    };
  },
});

export const insertBlockAfter = 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 {
      pageId: args.pageId,
      afterBlockId: args.afterBlockId,
      blockId: args.block.id,
    };
  },
});

export const replaceBlock = 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("块不存在");
    const ts = nowIso();
    await ctx.db.patch(block._id, {
      type: args.type,
      props: args.props,
      content: args.content,
      updated_at: ts,
    });
    return {
      blockId: args.blockId,
      updatedAt: ts,
    };
  },
});

export const deleteBlock = 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);
    const ts = nowIso();
    return {
      blockId: args.blockId,
      deletedAt: ts,
      updatedAt: ts,
    };
  },
});

export const listBlocksByPage = 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 createAgentActionStarted = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    actionId: v.string(),
    sessionId: v.string(),
    actionType: v.string(),
    actor: v.union(v.literal("human"), v.literal("agent"), v.literal("automation")),
    input: v.any(),
    startedAt: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    await ctx.db.insert("agent_actions", {
      id: args.actionId,
      workspace_id: args.workspaceId,
      session_id: args.sessionId,
      action_type: args.actionType,
      status: "running",
      actor: args.actor,
      input: args.input,
      output: null,
      error: null,
      started_at: args.startedAt,
      finished_at: null,
    });
    return { ok: true, actionId: args.actionId };
  },
});

export const markAgentActionSucceeded = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    actionId: v.string(),
    output: v.any(),
    finishedAt: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const row = await ctx.db
      .query("agent_actions")
      .withIndex("by_action_id", (q) => q.eq("id", args.actionId))
      .first();
    if (!row) throw new Error("action log not found");
    await ctx.db.patch(row._id, {
      status: "succeeded",
      output: args.output,
      finished_at: args.finishedAt,
    });
    return { ok: true };
  },
});

export const markAgentActionFailed = mutation({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    actionId: v.string(),
    error: v.string(),
    finishedAt: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    const row = await ctx.db
      .query("agent_actions")
      .withIndex("by_action_id", (q) => q.eq("id", args.actionId))
      .first();
    if (!row) throw new Error("action log not found");
    await ctx.db.patch(row._id, {
      status: "failed",
      error: args.error,
      finished_at: args.finishedAt,
    });
    return { ok: true };
  },
});

export const listAgentActionsBySession = query({
  args: {
    userId: v.string(),
    workspaceId: v.string(),
    sessionId: v.string(),
  },
  handler: async (ctx, args) => {
    await assertWorkspaceMember(ctx, args.userId, args.workspaceId);
    return await ctx.db
      .query("agent_actions")
      .withIndex("by_workspace_session", (q) =>
        q.eq("workspace_id", args.workspaceId).eq("session_id", args.sessionId),
      )
      .collect();
  },
});
