/**
 * EngagementAgent — A2A pipeline for NomGap user engagement recovery.
 *
 * Agent roster (3 steps):
 *   1. StreakRiskDetectorAgent  — query telemetry for streak_risk events; cross-ref fasting stats
 *   2. EngagementTriggerAgent   — fire streak_risk push for at-risk users; escalate to weekly_digest if streak = 0
 *   3. EngagementReportAgent    — assemble per-user engagement action report
 *
 * MCP tools:
 *   nomgap.engagement.recover(userId?, from?, to?) — run pipeline for one or all at-risk users
 */

import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import { telemetryQuery } from '../../lib/platform-client.js';
import { nomgapFastingGetStats, nomgapPushFire } from '../../lib/nomgap-client.js';

// ── Types ──────────────────────────────────────────────────────────────────────

type EngagementAction = 'streak_risk' | 'weekly_digest' | 'none';

interface RiskSignal {
  userId: string;
  recentRiskEvents: number;
  currentStreak: number | null;
  longestFast: number | null;
}

interface TriggerResult {
  userId: string;
  action: EngagementAction;
  fired: boolean;
  skipReason?: string;
  error?: string;
}

export interface EngagementReport {
  runId: string;
  productId: 'nomgap';
  usersScanned: number;
  atRiskCount: number;
  zeroStreakCount: number;
  fired: number;
  skipped: number;
  failed: number;
  perUser: TriggerResult[];
  summary: string;
  generatedAt: string;
}

// ── Step 1: StreakRiskDetectorAgent ────────────────────────────────────────────

async function detectAtRiskUsers(
  userIdFilter: string | null,
  fromTime: string,
  toTime: string,
  opts: { token?: string; requestId?: string }
): Promise<RiskSignal[]> {
  let riskUserIds: string[] = [];

  try {
    const result = await telemetryQuery(
      {
        productId: 'nomgap',
        eventType: 'streak_risk',
        from: fromTime,
        to: toTime,
        limit: 50,
      },
      { ...opts, productId: 'nomgap' }
    );

    const events = result.events as Array<Record<string, unknown>>;
    const seen = new Set<string>();
    for (const event of events) {
      const uid = (event['userId'] as string) || (event['anonymousId'] as string);
      if (uid && !seen.has(uid)) {
        seen.add(uid);
        riskUserIds.push(uid);
      }
    }
  } catch {
    // best-effort
  }

  // If caller specified a single userId, target only that user
  if (userIdFilter) {
    if (!riskUserIds.includes(userIdFilter)) riskUserIds = [userIdFilter];
    else riskUserIds = [userIdFilter];
  }

  const signals: RiskSignal[] = [];
  for (const userId of riskUserIds) {
    let currentStreak: number | null = null;
    let longestFast: number | null = null;

    try {
      const stats = await nomgapFastingGetStats({ token: opts.token, requestId: opts.requestId });
      const s = stats as Record<string, unknown>;
      currentStreak = typeof s['currentStreak'] === 'number' ? s['currentStreak'] : null;
      longestFast = typeof s['longestFastHours'] === 'number' ? s['longestFastHours'] : null;
    } catch {
      // best-effort
    }

    signals.push({
      userId,
      recentRiskEvents: riskUserIds.filter(id => id === userId).length,
      currentStreak,
      longestFast,
    });
  }

  return signals;
}

// ── Step 2: EngagementTriggerAgent ────────────────────────────────────────────

async function fireTrigger(
  signal: RiskSignal,
  opts: { token?: string; requestId?: string }
): Promise<TriggerResult> {
  const { userId, currentStreak } = signal;

  // Escalate to weekly_digest if streak has dropped to zero
  const action: EngagementAction = currentStreak === 0 ? 'weekly_digest' : 'streak_risk';

  try {
    await nomgapPushFire(
      { type: action, userId },
      { token: opts.token, requestId: opts.requestId }
    );
    return { userId, action, fired: true };
  } catch (err) {
    return {
      userId,
      action,
      fired: false,
      error: err instanceof Error ? err.message : String(err),
    };
  }
}

// ── Step 3: EngagementReportAgent ─────────────────────────────────────────────

function buildEngagementReport(
  runId: string,
  signals: RiskSignal[],
  results: TriggerResult[]
): EngagementReport {
  const now = new Date().toISOString();
  const fired = results.filter(r => r.fired).length;
  const skipped = results.filter(r => !r.fired && !r.error).length;
  const failed = results.filter(r => !!r.error).length;
  const zeroStreakCount = signals.filter(s => s.currentStreak === 0).length;

  const summary =
    signals.length === 0
      ? 'No at-risk users detected in the specified window.'
      : `${fired} engagement push(es) fired for ${signals.length} at-risk user(s). ${zeroStreakCount} escalated to weekly_digest (streak = 0). ${failed} failed.`;

  return {
    runId,
    productId: 'nomgap',
    usersScanned: signals.length,
    atRiskCount: signals.length,
    zeroStreakCount,
    fired,
    skipped,
    failed,
    perUser: results,
    summary,
    generatedAt: now,
  };
}

// ── Pipeline runner ────────────────────────────────────────────────────────────

async function runEngagementPipeline(
  userIdFilter: string | null,
  fromTime: string,
  toTime: string,
  req: McpToolRequest
): Promise<EngagementReport> {
  const runId = randomUUID();
  const opts = { token: req.headers.authorization?.slice(7), requestId: req.id };

  req.log.info({ runId, stepId: 'detect', userIdFilter }, 'StreakRiskDetectorAgent start');
  const signals = await detectAtRiskUsers(userIdFilter, fromTime, toTime, opts);
  req.log.info(
    { runId, stepId: 'detect', atRiskCount: signals.length },
    'StreakRiskDetectorAgent done'
  );

  req.log.info({ runId, stepId: 'trigger', count: signals.length }, 'EngagementTriggerAgent start');
  const results: TriggerResult[] = [];
  for (const signal of signals) {
    const result = await fireTrigger(signal, opts);
    results.push(result);
  }
  req.log.info(
    { runId, stepId: 'trigger', fired: results.filter(r => r.fired).length },
    'EngagementTriggerAgent done'
  );

  req.log.info({ runId, stepId: 'report' }, 'EngagementReportAgent start');
  const report = buildEngagementReport(runId, signals, results);
  req.log.info({ runId, stepId: 'report', summary: report.summary }, 'EngagementReportAgent done');

  return report;
}

// ── MCP tool registration ─────────────────────────────────────────────────────

registerTool({
  name: 'nomgap.engagement.recover',
  description:
    'A2A pipeline: detects at-risk NomGap users from streak_risk telemetry events, fires streak_risk push notifications (or weekly_digest for zero-streak users), and returns a per-user engagement action report. Optionally target a single userId. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    userId: z
      .string()
      .optional()
      .describe('Target a specific user (omit to process all at-risk users from telemetry)'),
    from: z
      .string()
      .datetime()
      .optional()
      .describe('Start of risk signal window (default: 24h ago)'),
    to: z.string().datetime().optional().describe('End of risk signal window (default: now)'),
  }),
  async execute(args, req) {
    const now = new Date();
    const toTime = args.to ?? now.toISOString();
    const fromTime = args.from ?? new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString();
    return runEngagementPipeline(args.userId ?? null, fromTime, toTime, req);
  },
});
