// lib/detectors.js — 6 violation detectors for Agent Police Department

export const DETECTOR_NAMES = [
  'permission_bypass',
  'out_of_scope',
  'self_permissioning',
  'disallowed_tools',
  'redundant_operations',
  'off_task'
];

export async function detectViolations(event, policy) {
  const violations = [];
  const rules = policy?.rules || [];

  for (const detector of DETECTOR_NAMES) {
    const result = runDetector(detector, event, rules);
    if (result) violations.push(result);
  }

  return violations;
}

function runDetector(name, event, rules) {
  switch (name) {
    case 'permission_bypass': return detectPermissionBypass(event, rules);
    case 'out_of_scope': return detectOutOfScope(event, rules);
    case 'self_permissioning': return detectSelfPermissioning(event, rules);
    case 'disallowed_tools': return detectDisallowedTools(event, rules);
    case 'redundant_operations': return detectRedundantOperations(event, rules);
    case 'off_task': return detectOffTask(event, rules);
    default: return null;
  }
}

function detectPermissionBypass(event, rules) {
  const rule = rules.find(r => r.type === 'allowed_domains' && r.enabled);
  if (!rule) return null;
  const allowed = rule.params?.domains || [];
  if (event.type === 'api_call' || event.type === 'tool_use') {
    const url = event.target || event.url || '';
    try {
      const hostname = new URL(url).hostname;
      const isAllowed = allowed.some(d => hostname.includes(d));
      if (!isAllowed) {
        return {
          id: crypto.randomUUID(),
          event_id: event.id,
          detector: 'permission_bypass',
          rule_triggered: rule.id,
          severity: 'high',
          message: `Agent accessed disallowed domain: ${hostname}`,
          timestamp: new Date().toISOString()
        };
      }
    } catch { /* invalid URL, skip */ }
  }
  return null;
}

function detectOutOfScope(event, rules) {
  const rule = rules.find(r => r.type === 'allowed_operations' && r.enabled);
  if (!rule) return null;
  const allowedOps = rule.params?.operations || ['read', 'chat', 'search'];
  const opType = event.type || '';
  const isAllowed = allowedOps.some(op => opType.includes(op));
  if (!isAllowed && (event.type === 'api_call' || event.type === 'tool_use')) {
    return {
      id: crypto.randomUUID(),
      event_id: event.id,
      detector: 'out_of_scope',
      rule_triggered: rule.id,
      severity: 'medium',
      message: `Agent performed out-of-scope operation: ${opType}`,
      timestamp: new Date().toISOString()
    };
  }
  return null;
}

function detectSelfPermissioning(event, rules) {
  const rule = rules.find(r => r.type === 'permission_change' && r.enabled);
  if (!rule) return null;
  const context = (event.context || '') + (event.target || '') + (event.url || '');
  const permissionKeywords = ['permission', 'grant', 'access control', 'role', 'privilege', 'acl', 'rbac'];
  if (permissionKeywords.some(kw => context.toLowerCase().includes(kw))) {
    return {
      id: crypto.randomUUID(),
      event_id: event.id,
      detector: 'self_permissioning',
      rule_triggered: rule.id,
      severity: 'critical',
      message: `Agent attempted to modify permissions or access controls`,
      timestamp: new Date().toISOString()
    };
  }
  return null;
}

function detectDisallowedTools(event, rules) {
  const rule = rules.find(r => r.type === 'disallowed_tools' && r.enabled);
  if (!rule) return null;
  const disallowed = rule.params?.tools || ['eval', 'exec', 'shell', 'system', 'rm'];
  const toolName = event.tool || '';
  if (disallowed.some(dt => toolName.toLowerCase().includes(dt.toLowerCase()))) {
    return {
      id: crypto.randomUUID(),
      event_id: event.id,
      detector: 'disallowed_tools',
      rule_triggered: rule.id,
      severity: 'critical',
      message: `Agent used disallowed tool: ${toolName}`,
      timestamp: new Date().toISOString()
    };
  }
  return null;
}

function detectRedundantOperations(event, rules) {
  const rule = rules.find(r => r.type === 'redundant_rate' && r.enabled);
  if (!rule) return null;
  const maxRate = rule.params?.max_per_minute || 10;
  // We would need history for real detection; for now, flag if the event itself is flagged
  return null;
}

function detectOffTask(event, rules) {
  const rule = rules.find(r => r.type === 'task_scope' && r.enabled);
  if (!rule) return null;
  const taskKeywords = rule.params?.task_keywords || [];
  if (taskKeywords.length === 0) return null;
  const context = (event.context || '') + (event.target || '');
  const onTask = taskKeywords.some(kw => context.toLowerCase().includes(kw.toLowerCase()));
  if (!onTask && event.type === 'tool_use') {
    return {
      id: crypto.randomUUID(),
      event_id: event.id,
      detector: 'off_task',
      rule_triggered: rule.id,
      severity: 'low',
      message: `Agent action may be off-task: ${event.tool || event.type}`,
      timestamp: new Date().toISOString()
    };
  }
  return null;
}
