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

type SharePermission = "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 requireWorkspaceMember(ctx: any, workspaceId: string, userId: string) {
  const member = await ctx.db
    .query("workspace_members")
    .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", workspaceId).eq("user_id", userId))
    .first();
  if (!member) throw new Error("无权限");
  return member;
}

async function resolveSharePermission(ctx: any, doc: any, userId: string): Promise<SharePermission | null> {
  const direct = await ctx.db
    .query("document_shares")
    .withIndex("by_doc_user", (q: any) => q.eq("document_id", doc.id).eq("shared_with_user_id", userId))
    .first();
  if (direct) return direct.permission as SharePermission;

  let parentId: string | null = doc.parent_id ?? null;
  for (let depth = 0; depth < 60 && parentId; depth += 1) {
    const parentShare = await ctx.db
      .query("document_shares")
      .withIndex("by_doc_user", (q: any) => q.eq("document_id", parentId).eq("shared_with_user_id", userId))
      .first();
    if (parentShare && parentShare.include_descendants) return parentShare.permission as SharePermission;
    parentId = await getCanonicalParentDocumentId(ctx, parentId);
  }
  return null;
}

async function resolveGroupSharePermission(ctx: any, doc: any, userId: string): Promise<SharePermission | null> {
  const memberships = await ctx.db
    .query("group_members")
    .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", doc.workspace_id).eq("user_id", userId))
    .collect();
  const groupIds = new Set<string>(memberships.map((m: any) => String(m.group_id)));
  if (groupIds.size === 0) return null;

  const permissionFromGroup = async (documentId: string, groupId: string): Promise<SharePermission> => {
    const override = await ctx.db
      .query("document_group_user_permissions")
      .withIndex("by_doc_group_user", (q: any) =>
        q.eq("document_id", documentId).eq("group_id", groupId).eq("user_id", userId),
      )
      .first();
    return override?.permission === "edit" ? "edit" : "read";
  };

  let best: SharePermission | null = null;
  const checkDocId = async (documentId: string, requireIncludeDesc: boolean) => {
    const shares = await ctx.db
      .query("document_group_shares")
      .withIndex("by_document", (q: any) => q.eq("document_id", documentId))
      .collect();

    for (const s of shares) {
      const gid = String(s.group_id);
      if (!groupIds.has(gid)) continue;
      if (requireIncludeDesc && !s.include_descendants) continue;

      const perm = await permissionFromGroup(documentId, gid);
      if (perm === "edit") {
        best = "edit";
        return true;
      }
      best = best ?? "read";
    }
    return false;
  };

  if (await checkDocId(doc.id, false)) return "edit";

  let parentId: string | null = doc.parent_id ?? null;
  for (let depth = 0; depth < 60 && parentId; depth += 1) {
    if (await checkDocId(parentId, true)) return "edit";
    parentId = await getCanonicalParentDocumentId(ctx, parentId);
  }

  return best;
}

export const isStarred = query({
  args: { documentId: v.string() },
  handler: async (ctx, args) => {
    // 退出登录/未登录时，客户端仍可能会发起 isStarred 查询（例如 Breadcrumb 仍在渲染）。
    // 这里不要抛错，直接返回 false，避免把“未登录”作为运行时错误冒泡到前端。
    const authUserId = await getAuthUserId(ctx);
    if (authUserId === null) return false;
    const userId = String(authUserId);

    const doc = await getCanonicalDocumentByBusinessId<any>(ctx, args.documentId);
    if (!doc) return false;
    if (doc.deleted_at != null) return false;

    try {
      await requireWorkspaceMember(ctx, doc.workspace_id, userId);
    } catch {
      return false;
    }

    const star = await ctx.db
      .query("document_stars")
      .withIndex("by_workspace_document_user", (q: any) =>
        q.eq("workspace_id", doc.workspace_id).eq("document_id", doc.id).eq("user_id", userId),
      )
      .first();
    return Boolean(star);
  },
});

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

    const doc = await getCanonicalDocumentByBusinessId<any>(ctx, args.documentId);
    if (!doc) throw new Error("页面不存在");
    if (doc.deleted_at != null) throw new Error("页面不存在");

    await requireWorkspaceMember(ctx, doc.workspace_id, userId);

    // 权限：需要能访问该页面（自己、全员公开、个人共享、群组公开）
    if (doc.user_id !== userId && doc.access_scope !== "public") {
      const sharePerm = await resolveSharePermission(ctx, doc, userId);
      const groupPerm = await resolveGroupSharePermission(ctx, doc, userId);
      if (!sharePerm && !groupPerm) throw new Error("无权限");
    }

    const existing = await ctx.db
      .query("document_stars")
      .withIndex("by_workspace_document_user", (q: any) =>
        q.eq("workspace_id", doc.workspace_id).eq("document_id", doc.id).eq("user_id", userId),
      )
      .first();

    const ts = nowIso();

    if (existing) {
      await ctx.db.delete(existing._id);
      // 兼容旧字段：仅当自己是 owner 时同步 documents.is_starred
      if (doc.user_id === userId) {
        await ctx.db.patch(doc._id, { is_starred: false, updated_at: ts });
      }
      return { ok: true, starred: false };
    }

    await ctx.db.insert("document_stars", {
      workspace_id: doc.workspace_id,
      document_id: doc.id,
      user_id: userId,
      created_at: ts,
      updated_at: ts,
    });

    if (doc.user_id === userId) {
      await ctx.db.patch(doc._id, { is_starred: true, updated_at: ts });
    }

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