/**
 * DailyBriefGenerationPipeline — A2A pipeline for MindLyst daily briefs.
 *
 * Agent roster (4 steps):
 *   1. MemoryCollectorAgent — fan-out: fetch recent memory items per brain
 *   2. SummaryAgent         — call extraction-service 'memory-insight' per brain
 *   3. BriefComposerAgent   — pure assembly of DailyBriefDoc (no I/O)
 *   4. DeliveryAgent        — upsert brief via POST /daily-briefs
 *
 * MCP tools:
 *   mindlyst.brief.generate(userId, date?) — run the full pipeline
 */

import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import {
  mindlystBrainsList,
  mindlystMemoryList,
  mindlystBriefCreate,
  type BrainDoc,
  type MemoryItemDoc,
  type DailyBriefDoc,
} from '../../lib/mindlyst-client.js';
import { extractionRun } from '../../lib/extraction-client.js';

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

interface BrainMemoryBucket {
  brainId: string;
  brainName: string;
  items: MemoryItemDoc[];
}

interface BrainSummaryResult {
  brainId: string;
  brainName: string;
  summary: string;
  highlights: string[];
}

export interface DailyBriefRunResult {
  runId: string;
  userId: string;
  date: string;
  totalBrainsConsidered: number;
  totalMemoriesConsidered: number;
  brief: DailyBriefDoc | null;
  stored: boolean;
  generatedAt: string;
}

// ── Step 1: MemoryCollectorAgent ──────────────────────────────────────────────

async function collectMemories(
  userId: string,
  lookbackHours: number,
  opts: { token?: string; requestId?: string; adminUserId?: string }
): Promise<BrainMemoryBucket[]> {
  const brainsResult = await mindlystBrainsList({}, opts);
  const brains: BrainDoc[] = brainsResult.items;

  const buckets: BrainMemoryBucket[] = [];

  for (const brain of brains) {
    const memResult = await mindlystMemoryList({ brainId: brain.id, limit: 20 }, opts);

    const cutoff = Date.now() - lookbackHours * 60 * 60 * 1000;
    const recentItems = memResult.items.filter(
      item => new Date(item.createdAt).getTime() >= cutoff
    );

    const sorted = recentItems
      .sort((a, b) => b.triageResult.urgencyScore - a.triageResult.urgencyScore)
      .slice(0, 10);

    if (sorted.length > 0) {
      buckets.push({ brainId: brain.id, brainName: brain.name, items: sorted });
    }
  }

  return buckets;
}

// ── Step 2: SummaryAgent ──────────────────────────────────────────────────────

async function summarizeBrain(
  bucket: BrainMemoryBucket,
  opts: { requestId?: string }
): Promise<BrainSummaryResult> {
  const text = bucket.items
    .map(item => `[${item.triageResult.contentType}] ${item.rawContent}`)
    .join('\n\n');

  try {
    const result = await extractionRun(
      { text, taskId: 'memory-insight' },
      { requestId: opts.requestId }
    );

    const summaryItems = result.extractions.filter(
      e => e.extraction_class === 'summary' || e.extraction_class === 'insight'
    );
    const entityItems = result.extractions.filter(
      e => e.extraction_class === 'entity' || e.extraction_class === 'theme'
    );

    const summary =
      summaryItems.length > 0
        ? summaryItems.map(e => e.extraction_text).join(' ')
        : bucket.items
            .slice(0, 3)
            .map(i => i.rawContent.slice(0, 80))
            .join(' | ');

    const highlights = entityItems.slice(0, 5).map(e => e.extraction_text);

    return { brainId: bucket.brainId, brainName: bucket.brainName, summary, highlights };
  } catch {
    // Fallback: top-3 items summarised inline
    const summary = bucket.items
      .slice(0, 3)
      .map(i => i.rawContent.slice(0, 100))
      .join(' | ');
    return { brainId: bucket.brainId, brainName: bucket.brainName, summary, highlights: [] };
  }
}

// ── Step 3: BriefComposerAgent (pure — no I/O) ───────────────────────────────

function composeBrief(
  userId: string,
  date: string,
  brainSummaries: BrainSummaryResult[],
  totalItems: number
): Omit<DailyBriefDoc, 'id' | 'createdAt'> {
  const topItems = brainSummaries
    .flatMap(b => b.highlights.slice(0, 2).map(h => ({ text: h, brainId: b.brainId })))
    .slice(0, 5);

  const summariesMap: Record<string, string> = {};
  for (const b of brainSummaries) {
    summariesMap[b.brainId] = b.summary;
  }

  const greeting =
    brainSummaries.length === 0
      ? 'No new memories captured today — a clean slate!'
      : `${brainSummaries.length} brain${brainSummaries.length > 1 ? 's' : ''} active with ${totalItems} item${totalItems !== 1 ? 's' : ''} to review.`;

  return {
    userId,
    productId: 'mindlyst',
    date,
    greeting,
    priorityItems: topItems,
    brainSummaries: summariesMap,
  };
}

// ── Step 4: DeliveryAgent ─────────────────────────────────────────────────────

async function storeBrief(
  draft: Omit<DailyBriefDoc, 'id' | 'createdAt'>,
  opts: { token?: string; requestId?: string; adminUserId?: string }
): Promise<{ brief: DailyBriefDoc; stored: boolean }> {
  try {
    const stored = await mindlystBriefCreate(
      {
        date: draft.date,
        greeting: draft.greeting,
        priorityItems: draft.priorityItems,
        brainSummaries: draft.brainSummaries,
      },
      opts
    );
    return { brief: stored, stored: true };
  } catch {
    return {
      brief: {
        id: `brief_local_${randomUUID().slice(0, 8)}`,
        createdAt: new Date().toISOString(),
        ...draft,
      },
      stored: false,
    };
  }
}

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

export async function runDailyBriefPipeline(
  userId: string,
  date: string,
  opts: { token?: string; requestId?: string }
): Promise<DailyBriefRunResult> {
  const runId = `run_brief_${randomUUID().slice(0, 8)}`;
  const generatedAt = new Date().toISOString();
  const clientOpts = { token: opts.token, requestId: opts.requestId, adminUserId: userId };

  // Step 1: collect memories (default 24h lookback)
  const buckets = await collectMemories(userId, 24, clientOpts).catch(
    () => [] as BrainMemoryBucket[]
  );

  const totalMemoriesConsidered = buckets.reduce((acc, b) => acc + b.items.length, 0);

  // Step 2: summarise each brain (parallel, best-effort)
  const summaryResults = await Promise.all(
    buckets.map(b => summarizeBrain(b, { requestId: opts.requestId }))
  );

  // Step 3: compose brief (pure)
  const draft = composeBrief(userId, date, summaryResults, totalMemoriesConsidered);

  // Step 4: store
  const { brief, stored } = await storeBrief(draft, clientOpts);

  return {
    runId,
    userId,
    date,
    totalBrainsConsidered: buckets.length,
    totalMemoriesConsidered,
    brief,
    stored,
    generatedAt,
  };
}

// ── MCP tool: mindlyst.brief.generate ─────────────────────────────────────────

registerTool({
  name: 'mindlyst.brief.generate',
  description: [
    'A2A daily brief pipeline for MindLyst.',
    'Fetches recent memory items across all brains (last 24 h), runs extraction memory-insight',
    'per brain to produce summaries + highlights, assembles a DailyBriefDoc, and stores it via',
    'POST /daily-briefs. Falls back gracefully if extraction-service is unavailable (uses raw',
    'item text instead). Returns the generated brief + metadata. Requires admin role.',
  ].join(' '),
  requiredRole: 'admin',
  inputSchema: z.object({
    userId: z.string().min(1).describe('MindLyst user ID to generate the brief for'),
    date: z
      .string()
      .regex(/^\d{4}-\d{2}-\d{2}$/)
      .optional()
      .describe('ISO date (YYYY-MM-DD); defaults to today'),
  }),
  async execute(args, req: McpToolRequest) {
    const token = req.headers.authorization?.slice(7);
    const date = args.date ?? new Date().toISOString().slice(0, 10);
    return runDailyBriefPipeline(args.userId, date, { token, requestId: req.id });
  },
});
