/**
 * Documents Queries
 */

import { paginationOptsValidator } from 'convex/server';
import { v } from 'convex/values';

import type { Doc } from '../_generated/dataModel';
import { query } from '../_generated/server';
import { getUserTeamIds } from '../lib/get_user_teams';
import { countItemsInOrg } from '../lib/helpers/count_items_in_org';
import { getAuthUserIdentity } from '../lib/rls/auth/get_auth_user_identity';
import { getOrganizationMember } from '../lib/rls/organization/get_organization_member';
import { hasTeamAccess } from '../lib/team_access';
import { isActiveDocument } from './_helpers';
import { listDocumentsPaginated as listDocumentsPaginatedHelper } from './list_documents_paginated';
import { searchDocumentsForMention as searchDocumentsForMentionHelper } from './search_documents_for_mention';
import { transformDocumentsBatch } from './transform_to_document_item';

export const approxCountDocuments = query({
  args: {
    organizationId: v.string(),
  },
  returns: v.number(),
  handler: async (ctx, args) => {
    const authUser = await getAuthUserIdentity(ctx);
    if (!authUser) return 0;
    try {
      await getOrganizationMember(ctx, args.organizationId, authUser);
    } catch {
      return 0;
    }
    return await countItemsInOrg(ctx.db, 'documents', args.organizationId);
  },
});

export const listDocuments = query({
  args: {
    organizationId: v.string(),
  },
  handler: async (ctx, args) => {
    const authUser = await getAuthUserIdentity(ctx);
    if (!authUser) {
      return [];
    }

    try {
      await getOrganizationMember(ctx, args.organizationId, authUser);
    } catch {
      return [];
    }

    const userTeamIds = await getUserTeamIds(ctx, authUser.userId);

    const documents: Doc<'documents'>[] = [];
    for await (const doc of ctx.db
      .query('documents')
      .withIndex('by_organizationId', (q) =>
        q.eq('organizationId', args.organizationId),
      )
      .order('desc')) {
      if (!isActiveDocument(doc)) continue;
      if (!hasTeamAccess(doc, userTeamIds)) continue;

      documents.push(doc);
    }

    return await transformDocumentsBatch(ctx, documents);
  },
});

/**
 * Point-query a single document by id, with the SAME access control as
 * `listDocuments` (org membership + team access + active lifecycle). Lets the
 * client fetch one document directly instead of pulling the whole collection
 * and filtering client-side. Returns null on not-found or any access failure
 * (never leaks a document from another org/team).
 */
export const getDocumentById = query({
  args: {
    documentId: v.id('documents'),
  },
  handler: async (ctx, args) => {
    const authUser = await getAuthUserIdentity(ctx);
    if (!authUser) return null;

    const doc = await ctx.db.get(args.documentId);
    if (!doc || !isActiveDocument(doc)) return null;

    try {
      await getOrganizationMember(ctx, doc.organizationId, authUser);
    } catch {
      return null;
    }

    const userTeamIds = await getUserTeamIds(ctx, authUser.userId);
    if (!hasTeamAccess(doc, userTeamIds)) return null;

    const [item] = await transformDocumentsBatch(ctx, [doc]);
    return item ?? null;
  },
});

/**
 * Title search for the chat composer's `@` knowledge-base mention picker.
 * Returns slim rows for RAG-indexed, user-accessible documents only (the
 * picker must not offer a document that the pinned-turn retrieval could not
 * actually search). Same auth contract as `listDocumentsPaginated`.
 */
export const searchDocumentsForMention = query({
  args: {
    organizationId: v.string(),
    query: v.string(),
  },
  handler: async (ctx, args) => {
    const authUser = await getAuthUserIdentity(ctx);
    if (!authUser) return [];

    try {
      await getOrganizationMember(ctx, args.organizationId, authUser);
    } catch {
      return [];
    }

    const userTeamIds = await getUserTeamIds(ctx, authUser.userId);

    return await searchDocumentsForMentionHelper(ctx, {
      organizationId: args.organizationId,
      term: args.query,
      userTeamIds,
    });
  },
});

export const listDocumentsPaginated = query({
  args: {
    paginationOpts: paginationOptsValidator,
    organizationId: v.string(),
    folderId: v.optional(v.id('folders')),
    sourceProvider: v.optional(v.string()),
    extension: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const authUser = await getAuthUserIdentity(ctx);
    if (!authUser) {
      return { page: [], isDone: true, continueCursor: '' };
    }

    try {
      await getOrganizationMember(ctx, args.organizationId, authUser);
    } catch {
      return { page: [], isDone: true, continueCursor: '' };
    }

    const userTeamIds = await getUserTeamIds(ctx, authUser.userId);

    return await listDocumentsPaginatedHelper(ctx, {
      ...args,
      userTeamIds,
    });
  },
});
