← back to Norma

app/api/intelligence/briefing/route.ts

89 lines

import { NextRequest, NextResponse } from 'next/server';
import { query } from '@/lib/db';
import { requireRole } from '@/lib/require-role';
import { getOrgId } from '@/lib/orgId';

const ALLOWED_STRATEGIES = new Set([
  'all',
  'legislative',
  'media',
  'coalition',
  'grassroots',
]);

/**
 * GET /api/intelligence/briefing
 * Query params:
 *   ?limit=    — rows to return (default 20, max 100)
 *   ?strategy= — all | legislative | media | coalition | grassroots
 * Headers:
 *   x-org-id  — scope results to a specific org
 * LEFT JOINs power_scores to attach score_breakdown.
 * Returns: { recommendations: [...] }
 * Ordered by priority ASC, power_score DESC.
 */
export async function GET(request: NextRequest) {
  const auth = requireRole(request, 'admin', 'staff');
  if (auth instanceof NextResponse) return auth;

  try {
    const { searchParams } = new URL(request.url);
    const orgId = auth.role === 'admin' ? getOrgId(request) : auth.orgId;
    const strategyParam = searchParams.get('strategy') || 'all';
    const strategy = ALLOWED_STRATEGIES.has(strategyParam) ? strategyParam : 'all';

    let limit = parseInt(searchParams.get('limit') || '20', 10);
    if (isNaN(limit) || limit < 1) limit = 20;
    if (limit > 100) limit = 100;

    const conditions: string[] = [];
    const values: unknown[] = [];
    let paramIndex = 1;

    if (orgId) {
      conditions.push(`wi.org_id = $${paramIndex}`);
      values.push(orgId);
      paramIndex++;
    }

    if (strategy !== 'all') {
      conditions.push(`wi.contact_strategy = $${paramIndex}`);
      values.push(strategy);
      paramIndex++;
    }

    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';

    const result = await query(
      `SELECT
         wi.org_id,
         wi.week_start,
         wi.entity_type,
         wi.entity_id,
         wi.entity_name,
         wi.power_score,
         wi.reasoning,
         wi.trigger_news_ids,
         wi.connection_chain,
         wi.priority,
         wi.contact_strategy,
         wi.status,
         wi.created_at,
         ps.score_breakdown
       FROM weekly_intelligence wi
       LEFT JOIN power_scores ps
         ON ps.entity_type = wi.entity_type
        AND ps.entity_id   = wi.entity_id
       ${whereClause}
       ORDER BY wi.priority ASC, wi.power_score DESC
       LIMIT $${paramIndex}`,
      [...values, limit],
    );

    return NextResponse.json({ recommendations: result.rows });
  } catch (err) {
    console.error('[api/intelligence/briefing] GET error:', (err as Error).message);
    return NextResponse.json({ error: 'Failed to fetch briefing' }, { status: 500 });
  }
}