// server/helpers.js
const nocodb = require('./nocodb');
const { DEFAULTS } = require('./config');

// Name lookup cache
const _nameCache = {};

// Clear cache periodically
setInterval(() => { Object.keys(_nameCache).forEach(k => delete _nameCache[k]); }, DEFAULTS.cacheTTLMs);

// Get a single record's display name
async function getRecordName(table, id) {
  if (!id) return null;
  const key = `${table}:${id}`;
  if (_nameCache[key] !== undefined) return _nameCache[key];
  try {
    const r = await nocodb.get(table, id);
    const name = r?.name || r?.title || r?.Name || null;
    _nameCache[key] = name;
    return name;
  } catch {
    _nameCache[key] = null;
    return null;
  }
}

// Batch resolve names for multiple IDs across tables
// Usage: await batchResolveNames({ brand: { table: 'Brands', ids: [1,2,3] }, user: { table: 'Users', ids: [4,5] } })
// Returns: { 'brand:1': 'BrandA', 'user:4': 'Alice', ... }
async function batchResolveNames(groups) {
  // groups is an object like: { brand: { table: 'Brands', ids: [1,2,3] }, user: { table: 'Users', ids: [4,5] } }
  const names = {};
  const fetches = [];

  for (const [prefix, { table, ids }] of Object.entries(groups)) {
    const uniqueIds = [...new Set(ids.filter(Boolean))];
    for (const id of uniqueIds) {
      const key = `${prefix}:${id}`;
      if (_nameCache[`${table}:${id}`] !== undefined) {
        names[key] = _nameCache[`${table}:${id}`];
      } else {
        fetches.push(
          getRecordName(table, id).then(name => { names[key] = name; })
        );
      }
    }
  }

  await Promise.all(fetches);
  return names;
}

// Parse comma-separated approver IDs
function parseApproverIds(str) {
  if (!str) return [];
  return str.split(',').map(s => s.trim()).filter(Boolean).map(Number);
}

// Safely parse JSON with fallback
function safeJsonParse(str, fallback = null) {
  if (!str || typeof str !== 'string') return fallback;
  try { return JSON.parse(str); } catch { return fallback; }
}

// Pick allowed fields from request body
function pickBodyFields(body, fields) {
  const data = {};
  for (const f of fields) {
    if (body[f] !== undefined) data[f] = body[f];
  }
  return data;
}

// Sanitize a value for use in NocoDB WHERE clauses
// Prevents injection by removing NocoDB query operators
function sanitizeWhereValue(val) {
  if (val === null || val === undefined) return '';
  const str = String(val);
  // Remove characters that could manipulate NocoDB query syntax
  return str.replace(/[~(),$]/g, '');
}

// Build user modules list from user record
function getUserModules(user, allModules) {
  if (user.role === 'superadmin') return allModules;
  if (user.modules) return safeJsonParse(user.modules, allModules);
  return allModules;
}

// Strip sensitive fields from user data before sending to client
const SENSITIVE_USER_FIELDS = ['password_hash', 'reset_token', 'reset_token_expires'];
function stripSensitiveFields(data) {
  if (Array.isArray(data)) return data.map(stripSensitiveFields);
  if (data && typeof data === 'object') {
    const out = { ...data };
    for (const f of SENSITIVE_USER_FIELDS) {
      delete out[f];
      delete out[f.replace(/_([a-z])/g, (_, c) => c.toUpperCase())];
    }
    return out;
  }
  return data;
}

// Get all team IDs for a user
async function getUserTeamIds(userId) {
  const entries = await nocodb.list('TeamMembers', { where: `(user_id,eq,${userId})`, limit: 200 });
  return new Set(entries.map(e => e.team_id));
}

// Get full visibility context for a user (team IDs + team project/campaign IDs)
async function getUserVisibilityContext(userId) {
  const myTeamIds = await getUserTeamIds(userId);
  if (myTeamIds.size === 0) return { myTeamIds, teamProjectIds: new Set(), teamCampaignIds: new Set() };

  // Fetch projects and campaigns that belong to the user's teams
  const allProjects = await nocodb.list('Projects', { limit: 2000 });
  const allCampaigns = await nocodb.list('Campaigns', { limit: 2000 });

  const teamProjectIds = new Set(
    allProjects.filter(p => p.team_id && myTeamIds.has(p.team_id)).map(p => p.Id)
  );
  const teamCampaignIds = new Set(
    allCampaigns.filter(c => c.team_id && myTeamIds.has(c.team_id)).map(c => c.Id)
  );

  return { myTeamIds, teamProjectIds, teamCampaignIds };
}

module.exports = {
  getRecordName,
  batchResolveNames,
  parseApproverIds,
  safeJsonParse,
  pickBodyFields,
  sanitizeWhereValue,
  getUserModules,
  stripSensitiveFields,
  getUserTeamIds,
  getUserVisibilityContext,
  _nameCache,
};
