const { logger } = require('@librechat/data-schemas');
const { PermissionBits, hasPermissions, ResourceType } = require('librechat-data-provider');
const { getEffectivePermissions } = require('~/server/services/PermissionService');
const { getAgents, getFiles } = require('~/models');

/**
 * Checks if user has access to a file through agent permissions
 * Files inherit permissions from agents authored by the file owner
 */
const checkAgentBasedFileAccess = async ({ userId, role, fileId, fileOwner }) => {
  try {
    /** Agents that have this file in their tool_resources */
    const agentsWithFile = await getAgents({
      $or: [
        { 'tool_resources.execute_code.file_ids': fileId },
        { 'tool_resources.file_search.file_ids': fileId },
        { 'tool_resources.image_edit.file_ids': fileId },
        { 'tool_resources.context.file_ids': fileId },
        { 'tool_resources.ocr.file_ids': fileId },
      ],
    });

    if (!agentsWithFile || agentsWithFile.length === 0) {
      return false;
    }

    const fileOwnerId = fileOwner?.toString();
    const userIdStr = userId.toString();
    for (const agent of agentsWithFile) {
      const agentAuthorId = agent.author?.toString();
      if (!agentAuthorId || !fileOwnerId || agentAuthorId !== fileOwnerId) {
        continue;
      }

      if (agentAuthorId === userIdStr) {
        logger.debug(`[fileAccess] User is author of agent ${agent.id}`);
        return true;
      }

      try {
        const permissions = await getEffectivePermissions({
          userId,
          role,
          resourceType: ResourceType.AGENT,
          resourceId: agent._id || agent.id,
        });

        if (hasPermissions(permissions, PermissionBits.VIEW)) {
          logger.debug(`[fileAccess] User ${userId} has VIEW permissions on agent ${agent.id}`);
          return true;
        }
      } catch (permissionError) {
        logger.warn(
          `[fileAccess] Permission check failed for agent ${agent.id}:`,
          permissionError.message,
        );
      }
    }

    return false;
  } catch (error) {
    logger.error('[fileAccess] Error checking agent-based access:', error);
    return false;
  }
};

const getTenantId = (value) => value?.toString?.() ?? null;

const denyFileAccess = (res) =>
  res.status(403).json({
    error: 'Forbidden',
    message: 'Insufficient permissions to access this file',
  });

/**
 * Middleware to check if user can access a file
 * Checks: 1) File ownership, 2) Agent-based access through a file-owner agent
 */
const fileAccess = async (req, res, next) => {
  try {
    const fileId = req.params.file_id;
    const userId = req.user?.id;
    const userRole = req.user?.role;
    if (!fileId) {
      return res.status(400).json({
        error: 'Bad Request',
        message: 'file_id is required',
      });
    }

    if (!userId) {
      return res.status(401).json({
        error: 'Unauthorized',
        message: 'Authentication required',
      });
    }

    const [file] = await getFiles({ file_id: fileId });
    if (!file) {
      return res.status(404).json({
        error: 'Not Found',
        message: 'File not found',
      });
    }

    const fileTenantId = getTenantId(file.tenantId);
    const userTenantId = getTenantId(req.user?.tenantId);
    // Tenant-scoped files are restricted to their tenant. Legacy files without
    // tenantId remain governed by owner/agent ACLs for non-tenant migrations.
    if (fileTenantId && fileTenantId !== userTenantId) {
      logger.warn(
        `[fileAccess] User ${userId} denied cross-tenant access to file ${fileId} (route ${req.originalUrl})`,
      );
      return denyFileAccess(res);
    }

    if (file.user && file.user.toString() === userId) {
      req.fileAccess = { file };
      return next();
    }

    /** Agent-based access (file inherits agent permissions) */
    const hasAgentAccess = await checkAgentBasedFileAccess({
      userId,
      role: userRole,
      fileId,
      fileOwner: file.user,
    });
    if (hasAgentAccess) {
      req.fileAccess = { file };
      return next();
    }

    logger.warn(
      `[fileAccess] User ${userId} denied access to file ${fileId} (route ${req.originalUrl})`,
    );
    return denyFileAccess(res);
  } catch (error) {
    logger.error('[fileAccess] Error checking file access:', error);
    return res.status(500).json({
      error: 'Internal Server Error',
      message: 'Failed to check file access permissions',
    });
  }
};

module.exports = {
  fileAccess,
};
