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

const permission = v.union(v.literal("read"), v.literal("edit"));

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

function normalizeUsername(raw: string): string {
  const username = String(raw ?? "").trim();
  if (!username) throw new Error("用户名不能为空");
  if (username.length < 2 || username.length > 32) throw new Error("用户名长度需为 2-32 个字符");
  if (/\s/.test(username)) throw new Error("用户名不能包含空格");
  return username;
}

async function resolveUserIdByUsername(ctx: any, rawUsername: string): Promise<{ userId: string; username: string }> {
  const username = normalizeUsername(rawUsername);

  // 说明：用户名存放在 Convex Auth 的 users.name；当前没有索引，这里用 filter 做扫描。
  const user = await ctx.db
    .query("users")
    .filter((q: any) => q.eq(q.field("name"), username))
    .first();
  if (!user) throw new Error("未找到该用户（对方可能未注册或未设置用户名）");

  return { userId: String(user._id), username: String(user.name ?? "") || username };
}

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_shares")
      .withIndex("by_document", (q: any) => q.eq("document_id", doc.id))
      .collect();

    const result = [];
    for (const share of shares) {
      const user = await ctx.db.get(share.shared_with_user_id as Id<"users">);
      result.push({
        userId: share.shared_with_user_id,
        username: user?.name ?? null,
        permission: share.permission,
        includeDescendants: share.include_descendants,
        disableDownload: Boolean((share as any).disable_download),
        disableCopy: Boolean((share as any).disable_copy),
        createdAt: share.created_at,
        updatedAt: share.updated_at,
      });
    }

    return result;
  },
});

export const upsert = mutation({
  args: {
    documentId: v.string(),
    username: v.string(),
    permission,
    includeDescendants: v.optional(v.boolean()),
    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 { userId: sharedWithUserId } = await resolveUserIdByUsername(ctx, args.username);
    if (sharedWithUserId === userId) throw new Error("不能共享给自己");

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

    // 确保对方成为 workspace member（否则无法看到该 workspace 的数据）
    const existingMember = await ctx.db
      .query("workspace_members")
      .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", doc.workspace_id).eq("user_id", sharedWithUserId))
      .first();
    if (!existingMember) {
      await ctx.db.insert("workspace_members", {
        workspace_id: doc.workspace_id,
        user_id: sharedWithUserId,
        role: "member",
        is_default: false,
        created_at: ts,
      });
    }

    const existingShare = await ctx.db
      .query("document_shares")
      .withIndex("by_doc_user", (q: any) =>
        q.eq("document_id", doc.id).eq("shared_with_user_id", sharedWithUserId),
      )
      .first();

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

    // 仅更新时间戳：共享不改变页面自身的 access_scope（避免把自己的页面从“私密”分区挪走）
    await ctx.db.patch(doc._id, { updated_at: ts });

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

export const remove = mutation({
  args: { documentId: v.string(), sharedWithUserId: 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_shares")
      .withIndex("by_doc_user", (q: any) =>
        q.eq("document_id", doc.id).eq("shared_with_user_id", args.sharedWithUserId),
      )
      .first();

    if (existingShare) {
      await ctx.db.delete(existingShare._id);
    }

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

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

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

    // 仅允许查看自己所在 workspace 的共享摘要
    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 incoming = await ctx.db
      .query("document_shares")
      .withIndex("by_workspace_shared_with", (q: any) => q.eq("workspace_id", args.workspaceId).eq("shared_with_user_id", userId))
      .collect();

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

    const outgoingByDoc = new Map<string, { includeDescendants: boolean; sharedWithCount: number }>();
    for (const row of outgoing) {
      const existing = outgoingByDoc.get(row.document_id);
      if (!existing) {
        outgoingByDoc.set(row.document_id, {
          includeDescendants: Boolean(row.include_descendants),
          sharedWithCount: 1,
        });
      } else {
        existing.includeDescendants = existing.includeDescendants || Boolean(row.include_descendants);
        existing.sharedWithCount += 1;
      }
    }

    return {
      incoming: incoming.map((r) => ({
        documentId: r.document_id,
        permission: r.permission,
        includeDescendants: r.include_descendants,
        createdBy: r.created_by,
        updatedAt: r.updated_at,
      })),
      outgoing: Array.from(outgoingByDoc.entries()).map(([documentId, v]) => ({
        documentId,
        includeDescendants: v.includeDescendants,
        sharedWithCount: v.sharedWithCount,
      })),
    };
  },
});

export const listMyShareRoots = query({
  args: {},
  handler: async (ctx) => {
    const userId = await requireUserId(ctx);

    const incomingRows = await ctx.db
      .query("document_shares")
      .withIndex("by_shared_with_user", (q: any) => q.eq("shared_with_user_id", userId))
      .collect();

    // 说明：只统计“我共享出去的”（自己是 created_by）；用于顶部共享面板展示摘要。
    const outgoingRows = await ctx.db
      .query("document_shares")
      .withIndex("by_created_by", (q: any) => q.eq("created_by", userId))
      .collect();

    // 说明：去重/合并：同一页面共享给多个用户时，只展示一条，并累计 sharedWithCount。
    const outgoingByKey = new Map<
      string,
      {
        workspaceId: string;
        documentId: string;
        includeDescendants: boolean;
        sharedWithCount: number;
        updatedAt: string;
      }
    >();

    for (const row of outgoingRows) {
      const key = `${row.workspace_id}:${row.document_id}`;
      const existing = outgoingByKey.get(key);
      if (!existing) {
        outgoingByKey.set(key, {
          workspaceId: row.workspace_id,
          documentId: row.document_id,
          includeDescendants: Boolean(row.include_descendants),
          sharedWithCount: 1,
          updatedAt: String(row.updated_at ?? ""),
        });
      } else {
        existing.includeDescendants = existing.includeDescendants || Boolean(row.include_descendants);
        existing.sharedWithCount += 1;
        const u = String(row.updated_at ?? "");
        if (u && u.localeCompare(existing.updatedAt) > 0) {
          existing.updatedAt = u;
        }
      }
    }

    const workspaceNameCache = new Map<string, string | null>();
    const getWorkspaceName = async (workspaceId: string): Promise<string | null> => {
      const cached = workspaceNameCache.get(workspaceId);
      if (workspaceNameCache.has(workspaceId)) return cached ?? null;
      const ws = await ctx.db
        .query("workspaces")
        .withIndex("by_workspace_id", (q: any) => q.eq("id", workspaceId))
        .first();
      const name = ws?.name ?? null;
      workspaceNameCache.set(workspaceId, name);
      return name;
    };

    const documentCache = new Map<
      string,
      { exists: boolean; deleted: boolean; title: string | null }
    >();
    const getDocumentInfo = async (
      documentId: string,
    ): Promise<{ exists: boolean; deleted: boolean; title: string | null }> => {
      const cached = documentCache.get(documentId);
      if (documentCache.has(documentId)) {
        return cached ?? { exists: false, deleted: true, title: null };
      }
      const doc = await getCanonicalDocumentByBusinessId<any>(ctx, documentId);
      const info = doc
        ? { exists: true, deleted: doc.deleted_at != null, title: (doc.title ?? null) as string | null }
        : { exists: false, deleted: true, title: null };
      documentCache.set(documentId, info);
      return info;
    };

    const incoming = [];
    for (const row of incomingRows) {
      // 说明：若对方仅写入了 share 但没把我加入 workspace，则这里会被文档/页面接口挡住；
      // 为避免面板出现“打不开的幽灵分享”，这里要求我确实是 workspace member。
      const member = await ctx.db
        .query("workspace_members")
        .withIndex("by_workspace_user", (q: any) => q.eq("workspace_id", row.workspace_id).eq("user_id", userId))
        .first();
      if (!member) continue;

      const docInfo = await getDocumentInfo(row.document_id);
      if (!docInfo.exists) continue;
      if (docInfo.deleted) continue;

      incoming.push({
        workspaceId: row.workspace_id,
        workspaceName: await getWorkspaceName(row.workspace_id),
        documentId: row.document_id,
        documentTitle: docInfo.title,
        permission: row.permission,
        includeDescendants: row.include_descendants,
        createdBy: row.created_by,
        updatedAt: row.updated_at,
      });
    }

    const outgoing = [];
    for (const v of outgoingByKey.values()) {
      const docInfo = await getDocumentInfo(v.documentId);
      if (!docInfo.exists) continue;
      if (docInfo.deleted) continue;

      outgoing.push({
        workspaceId: v.workspaceId,
        workspaceName: await getWorkspaceName(v.workspaceId),
        documentId: v.documentId,
        documentTitle: docInfo.title,
        includeDescendants: v.includeDescendants,
        sharedWithCount: v.sharedWithCount,
        updatedAt: v.updatedAt,
      });
    }

    incoming.sort((a: any, b: any) => String(b.updatedAt ?? "").localeCompare(String(a.updatedAt ?? "")));
    outgoing.sort((a: any, b: any) => String(b.updatedAt ?? "").localeCompare(String(a.updatedAt ?? "")));

    return { incoming, outgoing };
  },
});
