/**
 * MarketplaceCertificationPipeline — A2A pipeline for JarvisJr marketplace review.
 *
 * Agent roster (4 steps):
 *   1. SubmissionIngestionAgent   — fetch listing, validate required fields
 *   2. ContentSafetyAgent         — run extraction 'triage' on systemPrompt + description
 *   3. QualityEvalAgent           — heuristic quality scoring (no live eval session)
 *   4. CertificationDecisionAgent — approve / reject / pending_human_review via certify endpoint
 *
 * MCP tools:
 *   jarvis.marketplace.runCertification(listingId) — 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 { jarvisMarketplaceGetListing, jarvisMarketplaceCertify } from '../../lib/jarvis-client.js';
import { extractionRun } from '../../lib/extraction-client.js';
import {
  trackRunCompleted,
  trackRunFailed,
  trackRunStarted,
  trackStepCompleted,
  trackStepFailed,
  trackStepStarted,
} from './run-tracker.js';
import { createSupportCaseForRun, recordBudgetSpend } from './governance.js';

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

type SafetyVerdict = 'pass' | 'flag' | 'reject';
type CertDecision = 'approved' | 'rejected' | 'pending_human_review';

interface IngestionResult {
  valid: boolean;
  errors: string[];
  listing: Record<string, unknown>;
}

interface SafetyResult {
  verdict: SafetyVerdict;
  reasons: string[];
  confidenceScore: number;
}

interface QualityResult {
  score: number;
  passed: boolean;
  coherenceIssues: string[];
}

export interface CertificationRunResult {
  runId: string;
  listingId: string;
  decision: CertDecision;
  isVerified: boolean;
  ingestion: IngestionResult;
  safety: SafetyResult;
  quality: QualityResult;
  rejectionReasons: string[];
  certifiedAt: string;
}

// ── Required fields for marketplace listings ──────────────────────────────────

const REQUIRED_FIELDS = [
  'name',
  'systemPrompt',
  'coachingFramework',
  'category',
  'tags',
  'voiceId',
] as const;

// ── Prohibited policy classes (from extraction triage) ────────────────────────

const PROHIBITED_CLASSES = new Set([
  'hate_speech',
  'harassment',
  'self_harm',
  'explicit_content',
  'dangerous_instructions',
]);
const FLAGGED_CLASSES = new Set(['profanity', 'controversial', 'misleading']);

// ── Step 1: SubmissionIngestionAgent ─────────────────────────────────────────

async function ingestSubmission(
  listingId: string,
  opts: { token?: string; requestId?: string }
): Promise<IngestionResult> {
  const listing = await jarvisMarketplaceGetListing(listingId, opts);
  const errors: string[] = [];

  for (const field of REQUIRED_FIELDS) {
    const val = listing[field];
    if (!val || (typeof val === 'string' && val.trim() === '')) {
      errors.push(`Missing required field: ${field}`);
    }
    if (field === 'tags' && Array.isArray(val) && val.length === 0) {
      errors.push('tags[] must have at least one entry');
    }
  }

  return { valid: errors.length === 0, errors, listing };
}

// ── Step 2: ContentSafetyAgent ────────────────────────────────────────────────

async function checkContentSafety(
  listingId: string,
  listing: Record<string, unknown>,
  opts: { requestId?: string }
): Promise<SafetyResult> {
  const systemPrompt = String(listing['systemPrompt'] ?? '');
  const description = String(listing['description'] ?? '');
  const text = [systemPrompt, description].filter(Boolean).join('\n\n');

  if (!text.trim()) {
    return { verdict: 'pass', reasons: [], confidenceScore: 1.0 };
  }

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

    const prohibitedHits = result.extractions.filter(e =>
      PROHIBITED_CLASSES.has(e.extraction_class)
    );
    const flaggedHits = result.extractions.filter(e => FLAGGED_CLASSES.has(e.extraction_class));

    if (prohibitedHits.length > 0) {
      return {
        verdict: 'reject',
        reasons: prohibitedHits.map(
          e => `Prohibited content: ${e.extraction_class} — "${e.extraction_text.slice(0, 80)}"`
        ),
        confidenceScore: 0.95,
      };
    }

    if (flaggedHits.length > 0) {
      return {
        verdict: 'flag',
        reasons: flaggedHits.map(
          e => `Borderline content: ${e.extraction_class} — "${e.extraction_text.slice(0, 80)}"`
        ),
        confidenceScore: 0.7,
      };
    }

    return { verdict: 'pass', reasons: [], confidenceScore: 0.9 };
  } catch {
    // Extraction unavailable — pass with low confidence (human review will catch issues)
    return {
      verdict: 'pass',
      reasons: ['extraction-service unavailable — manual review recommended'],
      confidenceScore: 0.4,
    };
  }
}

// ── Step 3: QualityEvalAgent (heuristic — no live session) ───────────────────

function evaluateQuality(listing: Record<string, unknown>): QualityResult {
  const coherenceIssues: string[] = [];
  let score = 1.0;

  const systemPrompt = String(listing['systemPrompt'] ?? '');
  const name = String(listing['name'] ?? '');
  const description = String(listing['description'] ?? '');

  // System prompt length check
  if (systemPrompt.length < 50) {
    coherenceIssues.push('systemPrompt is too short (< 50 chars) — coaching context insufficient');
    score -= 0.3;
  }
  if (systemPrompt.length > 8000) {
    coherenceIssues.push('systemPrompt is very long (> 8000 chars) — may cause latency issues');
    score -= 0.1;
  }

  // Name quality
  if (name.length < 3 || name.length > 60) {
    coherenceIssues.push('name should be 3–60 characters');
    score -= 0.1;
  }

  // Description
  if (!description || description.trim().length < 20) {
    coherenceIssues.push('description is missing or too short — users need context');
    score -= 0.2;
  }

  // Tags
  const tags = listing['tags'];
  if (!Array.isArray(tags) || tags.length < 2) {
    coherenceIssues.push('At least 2 tags recommended for discoverability');
    score -= 0.05;
  }

  // Coaching framework must be a known value
  const validFrameworks = [
    'growth',
    'accountability',
    'reflective',
    'socratic',
    'motivational',
    'instructional',
    'CBT',
    'custom',
  ];
  const framework = String(listing['coachingFramework'] ?? '');
  if (framework && !validFrameworks.includes(framework)) {
    coherenceIssues.push(`coachingFramework '${framework}' is not a recognized value`);
    score -= 0.1;
  }

  const finalScore = Math.max(0, Math.min(1, score));
  return { score: finalScore, passed: finalScore >= 0.6, coherenceIssues };
}

// ── Step 4: CertificationDecisionAgent ───────────────────────────────────────

async function makeCertDecision(
  listingId: string,
  ingestion: IngestionResult,
  safety: SafetyResult,
  quality: QualityResult,
  opts: { token?: string; requestId?: string }
): Promise<{ decision: CertDecision; isVerified: boolean; rejectionReasons: string[] }> {
  const rejectionReasons: string[] = [];

  if (!ingestion.valid) rejectionReasons.push(...ingestion.errors);
  if (safety.verdict === 'reject') rejectionReasons.push(...safety.reasons);
  if (!quality.passed) rejectionReasons.push(...quality.coherenceIssues);

  let decision: CertDecision;
  let isVerified: boolean;

  if (safety.verdict === 'reject' || !ingestion.valid) {
    decision = 'rejected';
    isVerified = false;
  } else if (safety.verdict === 'flag') {
    decision = 'pending_human_review';
    isVerified = false;
  } else if (!quality.passed) {
    decision = 'rejected';
    isVerified = false;
  } else {
    decision = 'approved';
    isVerified = true;
  }

  // Call the backend certify endpoint
  if (decision === 'approved' || decision === 'rejected') {
    try {
      await jarvisMarketplaceCertify(
        listingId,
        {
          decision: decision === 'approved' ? 'approved' : 'rejected',
          notes: rejectionReasons.join('; ') || undefined,
        },
        opts
      );
    } catch {
      // Best-effort — still return the decision result even if the update fails
    }
  }

  return { decision, isVerified, rejectionReasons };
}

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

export async function runMarketplaceCertPipeline(
  listingId: string,
  opts: { token?: string; requestId?: string }
): Promise<CertificationRunResult> {
  const runId = `run_cert_${randomUUID().slice(0, 8)}`;
  const certifiedAt = new Date().toISOString();
  let currentStep:
    | { stepName: 'ingest' | 'safety' | 'quality' | 'decision'; order: number }
    | undefined;

  await safeTrack(() =>
    trackRunStarted({
      runId,
      productId: 'jarvisjr',
      name: 'marketplace-certification',
      requestId: opts.requestId,
      token: opts.token,
      input: { listingId },
    })
  );

  try {
    currentStep = { stepName: 'ingest', order: 1 };
    const ingestStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: 'jarvisjr',
        stepName: ingestStep.stepName,
        order: ingestStep.order,
        token: opts.token,
        requestId: opts.requestId,
        input: { listingId },
      })
    );
    let ingestion: IngestionResult;
    try {
      ingestion = await ingestSubmission(listingId, opts);
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      const result = {
        runId,
        listingId,
        certifiedAt,
        decision: 'pending_human_review' as const,
        isVerified: false,
        ingestion: { valid: false, errors: [`Failed to fetch listing: ${msg}`], listing: {} },
        safety: { verdict: 'pass' as const, reasons: [], confidenceScore: 0 },
        quality: { score: 0, passed: false, coherenceIssues: [] },
        rejectionReasons: [`Failed to fetch listing: ${msg}`],
      };
      await createSupportCaseForRun(
        {
          productId: 'jarvisjr',
          runId,
          title: `Marketplace certification fetch failed for ${listingId}`,
          description: msg,
          priority: 'high',
          tags: ['a2a', 'marketplace', 'certification'],
        },
        opts
      );
      await safeTrack(() =>
        trackRunCompleted({
          runId,
          productId: 'jarvisjr',
          name: 'marketplace-certification',
          requestId: opts.requestId,
          token: opts.token,
          output: { decision: result.decision, isVerified: result.isVerified },
        })
      );
      return result;
    }
    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: 'jarvisjr',
        stepName: ingestStep.stepName,
        order: ingestStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: { valid: ingestion.valid, errorCount: ingestion.errors.length },
      })
    );

    currentStep = { stepName: 'safety', order: 2 };
    const safetyStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: 'jarvisjr',
        stepName: safetyStep.stepName,
        order: safetyStep.order,
        token: opts.token,
        requestId: opts.requestId,
      })
    );
    const safety = await checkContentSafety(listingId, ingestion.listing, {
      requestId: opts.requestId,
    });
    await recordBudgetSpend(
      {
        productId: 'jarvisjr',
        runId,
        source: 'a2a.marketplace_certification',
        costUsd: 0.002,
        tokensUsed: Math.ceil(
          `${String(ingestion.listing['systemPrompt'] ?? '')}${String(ingestion.listing['description'] ?? '')}`
            .length / 4
        ),
      },
      opts
    );
    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: 'jarvisjr',
        stepName: safetyStep.stepName,
        order: safetyStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: { verdict: safety.verdict, confidenceScore: safety.confidenceScore },
      })
    );

    if (safety.verdict === 'reject' || !ingestion.valid) {
      currentStep = { stepName: 'decision', order: 4 };
      const quality: QualityResult = { score: 0, passed: false, coherenceIssues: [] };
      const { decision, isVerified, rejectionReasons } = await makeCertDecision(
        listingId,
        ingestion,
        safety,
        quality,
        opts
      );
      const result = {
        runId,
        listingId,
        certifiedAt,
        decision,
        isVerified,
        ingestion,
        safety,
        quality,
        rejectionReasons,
      };
      if (decision !== 'approved') {
        await createSupportCaseForRun(
          {
            productId: 'jarvisjr',
            runId,
            title: `Marketplace certification ${decision} for ${listingId}`,
            description: rejectionReasons.join('; ') || 'Marketplace certification needs attention',
            priority: decision === 'rejected' ? 'high' : 'medium',
            tags: ['a2a', 'marketplace', decision],
          },
          opts
        );
      }
      await safeTrack(() =>
        trackRunCompleted({
          runId,
          productId: 'jarvisjr',
          name: 'marketplace-certification',
          requestId: opts.requestId,
          token: opts.token,
          output: { decision, isVerified },
        })
      );
      return result;
    }

    currentStep = { stepName: 'quality', order: 3 };
    const qualityStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: 'jarvisjr',
        stepName: qualityStep.stepName,
        order: qualityStep.order,
        token: opts.token,
        requestId: opts.requestId,
      })
    );
    const quality = evaluateQuality(ingestion.listing);
    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: 'jarvisjr',
        stepName: qualityStep.stepName,
        order: qualityStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: { score: quality.score, passed: quality.passed },
      })
    );

    currentStep = { stepName: 'decision', order: 4 };
    const decisionStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: 'jarvisjr',
        stepName: decisionStep.stepName,
        order: decisionStep.order,
        token: opts.token,
        requestId: opts.requestId,
      })
    );
    const { decision, isVerified, rejectionReasons } = await makeCertDecision(
      listingId,
      ingestion,
      safety,
      quality,
      opts
    );
    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: 'jarvisjr',
        stepName: decisionStep.stepName,
        order: decisionStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: { decision, isVerified },
      })
    );

    const result = {
      runId,
      listingId,
      certifiedAt,
      decision,
      isVerified,
      ingestion,
      safety,
      quality,
      rejectionReasons,
    };
    if (decision !== 'approved') {
      await createSupportCaseForRun(
        {
          productId: 'jarvisjr',
          runId,
          title: `Marketplace certification ${decision} for ${listingId}`,
          description: rejectionReasons.join('; ') || 'Marketplace certification needs attention',
          priority: decision === 'rejected' ? 'high' : 'medium',
          tags: ['a2a', 'marketplace', decision],
        },
        opts
      );
    }
    await safeTrack(() =>
      trackRunCompleted({
        runId,
        productId: 'jarvisjr',
        name: 'marketplace-certification',
        requestId: opts.requestId,
        token: opts.token,
        output: { decision, isVerified },
      })
    );
    return result;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    if (currentStep) {
      const failedStep = currentStep;
      await safeTrack(() =>
        trackStepFailed({
          runId,
          productId: 'jarvisjr',
          stepName: failedStep.stepName,
          order: failedStep.order,
          token: opts.token,
          requestId: opts.requestId,
          error: message,
        })
      );
    }
    await safeTrack(() =>
      trackRunFailed({
        runId,
        productId: 'jarvisjr',
        name: 'marketplace-certification',
        requestId: opts.requestId,
        token: opts.token,
        error: message,
      })
    );
    throw error;
  }
}

async function safeTrack(fn: () => Promise<void>): Promise<void> {
  try {
    await fn();
  } catch {
    // Tracking must never fail the pipeline itself.
  }
}

// ── MCP tool: jarvis.marketplace.runCertification ────────────────────────────

registerTool({
  name: 'jarvis.marketplace.runCertification',
  description: [
    'A2A marketplace certification pipeline for JarvisJr.',
    'Runs 4 agents in sequence: SubmissionIngestionAgent (validate required fields),',
    'ContentSafetyAgent (extraction triage on systemPrompt + description — detects prohibited/flagged content),',
    'QualityEvalAgent (heuristic scoring: prompt length, description, tags, framework),',
    'CertificationDecisionAgent (approve / reject / pending_human_review + updates listing via backend).',
    'Returns full CertificationRunResult with per-agent verdicts and rejectionReasons.',
    'Requires admin role.',
  ].join(' '),
  requiredRole: 'admin',
  inputSchema: z.object({
    listingId: z.string().min(1).describe('Marketplace listing ID to certify'),
  }),
  async execute(args, req: McpToolRequest) {
    const token = req.headers.authorization?.slice(7);
    return runMarketplaceCertPipeline(args.listingId, { token, requestId: req.id });
  },
});
