// lib/connection-grant.js — post-connect grant foundation.
//
// After a connection verifies, the operator chooses which agents get the tools it
// unlocks. This is the missing UX glue, not a missing primitive: it composes the
// connection manifest's `unlocks_tool_slugs` with the existing per-agent
// enforcement layer (agent_tool_permissions via setAgentToolPermission).
//
// SAFE DEFAULTS (load-bearing): a granted tool's permission mode is derived from
// its registry risk tier — NEVER from the caller:
//   low / medium            → 'auto'              (runs without a per-call gate)
//   high / irreversible_*   → 'approval_required' (always stops for a human)
// irreversible_external additionally ALWAYS gates at execution regardless of the
// stored mode, so 'approval_required' here is belt-and-suspenders, not the only
// guard. Unknown tools (no registry descriptor) fall back to 'approval_required'.

import { getDb, nowIso, newId } from './db.js';
import { listMarketplaceTools } from './tools/registry.js';
import { getConnection } from './connections.js';
import { setAgentToolPermission } from './runtime.js';

const AUTO_RISK = new Set(['low', 'medium']);

/** Resolve the safe default permission mode for a tool by its risk tier. */
export function defaultModeForRisk(riskLevel) {
  return AUTO_RISK.has(riskLevel) ? 'auto' : 'approval_required';
}

function writeAudit(meta) {
  try {
    getDb().prepare(`
      INSERT INTO audit_logs (id, at, type, summary, status, meta)
      VALUES (?, ?, ?, ?, ?, ?)
    `).run(
      newId('audit'),
      nowIso(),
      'connection.grant',
      `Granted ${meta.service_key} tools to ${meta.agent_ids.length} agent(s)`,
      'ok',
      JSON.stringify(meta),
    );
  } catch {
    // best-effort
  }
}

/**
 * Grant a connection's unlocked tools to one or more agents.
 *
 * @param {object} args
 * @param {string} [args.service_key]      connection slug; its manifest supplies unlocks_tool_slugs
 * @param {string[]} [args.tool_slugs]     explicit tool slugs (overrides service_key lookup)
 * @param {string[]} args.agent_ids        agents to grant to
 * @returns {{ ok, granted: Array, skipped: Array, agent_ids: string[] }}
 */
export function grantConnectionToAgents(args = {}) {
  const agentIds = Array.isArray(args.agent_ids) ? args.agent_ids.filter(Boolean).map(String) : [];
  if (!agentIds.length) return { ok: false, error: 'no_agents', granted: [], skipped: [] };

  // Resolve the unlocked tool slugs: explicit list wins, else the connection manifest.
  let toolSlugs = Array.isArray(args.tool_slugs) ? args.tool_slugs.filter(Boolean).map(String) : null;
  let serviceKey = args.service_key || null;
  if (!toolSlugs) {
    if (!serviceKey) return { ok: false, error: 'no_service_or_tools', granted: [], skipped: [] };
    const connection = getConnection(serviceKey);
    if (!connection) return { ok: false, error: 'connection_not_found', granted: [], skipped: [] };
    const manifest = connection.manifest || {};
    toolSlugs = Array.isArray(manifest.unlocks_tool_slugs) ? manifest.unlocks_tool_slugs.map(String) : [];
  }

  // Risk map from the live registry (static tools + dynamic composites/connectors).
  const riskByTool = new Map();
  for (const tool of listMarketplaceTools()) riskByTool.set(tool.name, tool.risk_level);

  const db = getDb();
  const granted = [];
  const skipped = [];

  for (const agentId of agentIds) {
    const agentRow = db.prepare('SELECT id, tools FROM agents WHERE id = ?').get(agentId);
    if (!agentRow) {
      skipped.push({ agent_id: agentId, reason: 'agent_not_found' });
      continue;
    }
    // Union the declared toolset so the agent card reflects the new tools.
    let declared = [];
    try { declared = JSON.parse(agentRow.tools || '[]'); } catch { declared = []; }
    if (!Array.isArray(declared)) declared = [];
    const declaredSet = new Set(declared);

    for (const slug of toolSlugs) {
      const risk = riskByTool.get(slug);
      const mode = defaultModeForRisk(risk);
      try {
        setAgentToolPermission(agentId, slug, mode);
        declaredSet.add(slug);
        granted.push({ agent_id: agentId, tool: slug, mode, risk_level: risk || null });
      } catch (err) {
        skipped.push({ agent_id: agentId, tool: slug, reason: String(err?.message || err) });
      }
    }
    db.prepare('UPDATE agents SET tools = ? WHERE id = ?').run(JSON.stringify([...declaredSet]), agentId);
  }

  writeAudit({ service_key: serviceKey, agent_ids: agentIds, tool_slugs: toolSlugs, granted_count: granted.length });
  return { ok: true, granted, skipped, agent_ids: agentIds };
}
