import { mutation, query } from "./_generated/server";
import { v } from "convex/values";
import { getAuthUserId } from "@convex-dev/auth/server";
import { nowIso } from "./_utils/time";
import { requireCanonicalOwnedDocument } from "./_utils/documentRecord";

type Permission = "read" | "edit";

async function requireUserId(ctx: any): Promise<string> {
  const userId = await getAuthUserId(ctx);
  if (userId === null) throw new Error("未登录");
  return String(userId);
}

async function requireGroup(ctx: any, groupId: string) {
  const group = await ctx.db
    .query("groups")
    .withIndex("by_group_id", (q: any) => q.eq("id", groupId))
    .first();
  if (!group) throw new Error("群组不存在");
  return group;
}

async function requireGroupMember(ctx: any, groupId: string, userId: string) {
  const membership = await ctx.db
    .query("group_members")
    .withIndex("by_group_user", (q: any) => q.eq("group_id", groupId).eq("user_id", userId))
    .first();
  if (!membership) throw new Error("无权限（你不在该群组内）");
  return membership;
}

export const listByDocument = query({
  args: { documentId: v.string() },
  handler: async (ctx, args) => {
    const userId = await requireUserId(ctx);
    const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId);

    const shares = await ctx.db
      .query("document_group_shares")
      .withIndex("by_document", (q: any) => q.eq("document_id", doc.id))
      .collect();

    const result = [];
    for (const share of shares) {
      const group = await ctx.db
        .query("groups")
        .withIndex("by_group_id", (q: any) => q.eq("id", share.group_id))
        .first();

      const perms = await ctx.db
        .query("document_group_user_permissions")
        .withIndex("by_workspace_group", (q: any) =>
          q.eq("workspace_id", doc.workspace_id).eq("group_id", share.group_id),
        )
        .collect();

      const editableUserIds = perms
        .filter((p: any) => p.document_id === doc.id && p.permission === "edit")
        .map((p: any) => String(p.user_id));

      result.push({
        groupId: share.group_id,
        groupName: group?.name ?? null,
        includeDescendants: share.include_descendants,
        disableDownload: Boolean((share as any).disable_download),
        disableCopy: Boolean((share as any).disable_copy),
        editableUserIds,
        updatedAt: share.updated_at,
      });
    }

    return result;
  },
});

export const upsert = mutation({
  args: {
    documentId: v.string(),
    groupId: v.string(),
    includeDescendants: v.optional(v.boolean()),
    editableUserIds: v.array(v.string()),
    disableDownload: v.optional(v.boolean()),
    disableCopy: v.optional(v.boolean()),
  },
  handler: async (ctx, args) => {
    const userId = await requireUserId(ctx);
    const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId);
    const group = await requireGroup(ctx, args.groupId);
    if (group.workspace_id !== doc.workspace_id) throw new Error("群组不属于当前工作空间");
    await requireGroupMember(ctx, args.groupId, userId);

    const ts = nowIso();
    const includeDescendants = Boolean(args.includeDescendants);
    const disableDownload = Boolean(args.disableDownload);
    const disableCopy = Boolean(args.disableCopy);

    const existingShare = await ctx.db
      .query("document_group_shares")
      .withIndex("by_doc_group", (q: any) => q.eq("document_id", doc.id).eq("group_id", args.groupId))
      .first();

    if (existingShare) {
      await ctx.db.patch(existingShare._id, {
        include_descendants: includeDescendants,
        disable_download: disableDownload,
        disable_copy: disableCopy,
        updated_at: ts,
      });
    } else {
      await ctx.db.insert("document_group_shares", {
        workspace_id: doc.workspace_id,
        group_id: args.groupId,
        document_id: doc.id,
        include_descendants: includeDescendants,
        disable_download: disableDownload,
        disable_copy: disableCopy,
        created_by: userId,
        created_at: ts,
        updated_at: ts,
      });
    }

    // 更新成员权限：默认只读，仅对可编辑用户写入 override（edit）。
    const existingPerms = await ctx.db
      .query("document_group_user_permissions")
      .withIndex("by_workspace_group", (q: any) =>
        q.eq("workspace_id", doc.workspace_id).eq("group_id", args.groupId),
      )
      .collect();

    const targetSet = new Set(args.editableUserIds.map(String));
    for (const p of existingPerms) {
      if (p.document_id !== doc.id) continue;
      const uid = String(p.user_id);
      if (!targetSet.has(uid)) {
        await ctx.db.delete(p._id);
      }
    }

    for (const uid of targetSet) {
      const existing = existingPerms.find(
        (p: any) => p.document_id === doc.id && String(p.user_id) === uid,
      );
      if (existing) {
        if (existing.permission !== "edit") {
          await ctx.db.patch(existing._id, { permission: "edit" as Permission, updated_at: ts });
        }
      } else {
        await ctx.db.insert("document_group_user_permissions", {
          workspace_id: doc.workspace_id,
          group_id: args.groupId,
          document_id: doc.id,
          user_id: uid,
          permission: "edit" as Permission,
          created_at: ts,
          updated_at: ts,
        });
      }
    }

    await ctx.db.patch(doc._id, { updated_at: ts });

    return { ok: true };
  },
});

export const remove = mutation({
  args: { documentId: v.string(), groupId: v.string() },
  handler: async (ctx, args) => {
    const userId = await requireUserId(ctx);
    const doc = await requireCanonicalOwnedDocument(ctx, args.documentId, userId);

    const existingShare = await ctx.db
      .query("document_group_shares")
      .withIndex("by_doc_group", (q: any) => q.eq("document_id", doc.id).eq("group_id", args.groupId))
      .first();
    if (existingShare) {
      await ctx.db.delete(existingShare._id);
    }

    const perms = await ctx.db
      .query("document_group_user_permissions")
      .withIndex("by_workspace_group", (q: any) =>
        q.eq("workspace_id", doc.workspace_id).eq("group_id", args.groupId),
      )
      .collect();

    for (const p of perms) {
      if (p.document_id !== doc.id) continue;
      await ctx.db.delete(p._id);
    }

    await ctx.db.patch(doc._id, { updated_at: nowIso() });
    return { ok: true };
  },
});

export const listPublicByWorkspace = query({
  args: { workspaceId: v.string() },
  handler: async (ctx, args) => {
    const userId = await requireUserId(ctx);

    const member = await ctx.db
      .query("workspace_members")
      .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", args.workspaceId).eq("user_id", userId))
      .first();
    if (!member) throw new Error("无权限");

    const myGroups = await ctx.db
      .query("group_members")
      .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", args.workspaceId).eq("user_id", userId))
      .collect();

    const myGroupIds = new Set<string>(myGroups.map((g: any) => String(g.group_id)));

    const myCreatedShares = await ctx.db
      .query("document_group_shares")
      .withIndex("by_workspace_created_by", (q: any) => q.eq("workspace_id", args.workspaceId).eq("created_by", userId))
      .collect();

    const visibleGroupIds = new Set<string>(myGroupIds);
    myCreatedShares.forEach((s: any) => visibleGroupIds.add(String(s.group_id)));

    const groups = await ctx.db
      .query("groups")
      .withIndex("by_workspace", (q: any) => q.eq("workspace_id", args.workspaceId))
      .collect();

    const groupNameById = new Map<string, string>();
    for (const g of groups) {
      if (visibleGroupIds.has(String(g.id))) {
        groupNameById.set(String(g.id), String(g.name ?? ""));
      }
    }

    const sharesByGroup = [];
    for (const groupId of visibleGroupIds) {
      const rows = await ctx.db
        .query("document_group_shares")
        .withIndex("by_workspace_group", (q: any) => q.eq("workspace_id", args.workspaceId).eq("group_id", groupId))
        .collect();

      if (rows.length === 0) continue;

      const docMap = new Map<string, { includeDescendants: boolean }>();
      for (const r of rows) {
        const existing = docMap.get(r.document_id);
        if (!existing) {
          docMap.set(r.document_id, { includeDescendants: Boolean(r.include_descendants) });
        } else {
          existing.includeDescendants = existing.includeDescendants || Boolean(r.include_descendants);
        }
      }

      sharesByGroup.push({
        groupId,
        groupName: groupNameById.get(groupId) ?? null,
        documents: Array.from(docMap.entries()).map(([documentId, v]) => ({
          documentId,
          includeDescendants: v.includeDescendants,
        })),
      });
    }

    // 按群组名称排序，便于折叠展示
    sharesByGroup.sort((a: any, b: any) => String(a.groupName ?? "").localeCompare(String(b.groupName ?? "")));

    return sharesByGroup;
  },
});
