/**
 * Phase 7 — Social Adaptation Engine
 * ----------------------------------
 * يجمع تقارير العقد (Evolution_Report.json لكل عقدة)،
 * يحسب مؤشرات وعي جماعي:
 *  - collectiveStability (متوسط الاستقرار)
 *  - collaborationIndex (مدى التعاون الحقيقي بين العقد)
 *  - metaResonance (تشابه/تماهي الميتا-تفكير بين العقد — باستعمال شبه-تشابه كسيني)
 *  - nextEvolutionDirective (Expand / Contract / Freeze / Merge)
 *
 * يكتب Integration_Report.json + يدعم استقبال تقارير عبر Bus/CFP.
 */

import fs from 'node:fs';
import path from 'node:path';
import { Bus } from './integration_bus';
import { buildMessage, validateMessage, isReportBroadcast, CFPMessage } from './cfp_protocol';

// =================== Types ===================

export interface NodeReport {
  nodeId: string;
  // محتوى Evolution_Report.json (Phase 6 Runner)
  metrics: {
    adaptationRate: number; // 0..1
    stabilityIndex: number; // 0..1
    metaCognitionLevel: number; // 0..1
    nextRecommendedMutation: 'Expand' | 'Contract' | 'Freeze';
  };
  registrySnapshot?: Array<{ policyId: string; weight: number }>;
  // optional: ملف المصدر
  sourcePath?: string;
  trust?: number; // 0..1 default 0.7
}

export interface IntegrationReport {
  timestamp: string;
  nodes: Array<{ nodeId: string; trust: number }>;
  metrics: {
    collectiveStability: number;
    collaborationIndex: number;
    metaResonance: number;
    nextEvolutionDirective: 'Expand' | 'Contract' | 'Freeze' | 'Merge';
  };
  notes: {
    interpretation: string;
    guidance: string;
  };
}

// =================== Utils ===================

function clamp01(x: number) {
  return Math.max(0, Math.min(1, x));
}
function mean(xs: number[]) {
  return xs.length ? xs.reduce((a, b) => a + b, 0) / xs.length : 0;
}

function vectorizeRegistry(reg?: Array<{ policyId: string; weight: number }>): Map<string, number> {
  const m = new Map<string, number>();
  (reg ?? []).forEach((r) => m.set(r.policyId, r.weight));
  return m;
}

function cosineSimilarity(a: Map<string, number>, b: Map<string, number>) {
  // union keys
  const keys = new Set<string>([...Array.from(a.keys()), ...Array.from(b.keys())]);
  let dot = 0,
    na = 0,
    nb = 0;
  const keysArr = Array.from(keys);
  for (let i = 0; i < keysArr.length; i++) {
    const k = keysArr[i];
    const va = a.get(k) ?? 0;
    const vb = b.get(k) ?? 0;
    dot += va * vb;
    na += va * va;
    nb += vb * vb;
  }
  if (na === 0 || nb === 0) return 0;
  return clamp01(dot / (Math.sqrt(na) * Math.sqrt(nb)));
}

// =================== Core Computations ===================

export function computeCollectiveStability(nodes: NodeReport[]): number {
  const s = nodes.map((n) => clamp01(n.metrics.stabilityIndex));
  return clamp01(mean(s));
}

/**
 * collaborationIndex:
 * - مزيج من (تشابه السياسات) × (متوسط الثقة بين الأزواج).
 * - بنحسب تشابه كسيني للـ registrySnapshot بين كل زوج ونضرب في trust المتوسط.
 */
export function computeCollaborationIndex(nodes: NodeReport[]): number {
  if (nodes.length <= 1) return 0;
  let acc = 0,
    pairs = 0;

  for (let i = 0; i < nodes.length; i++) {
    for (let j = i + 1; j < nodes.length; j++) {
      const A = vectorizeRegistry(nodes[i].registrySnapshot);
      const B = vectorizeRegistry(nodes[j].registrySnapshot);
      const sim = cosineSimilarity(A, B); // 0..1
      const trustAvg = mean([nodes[i].trust ?? 0.7, nodes[j].trust ?? 0.7]); // 0..1
      acc += sim * trustAvg;
      pairs++;
    }
  }
  return pairs > 0 ? clamp01(acc / pairs) : 0;
}

/**
 * metaResonance:
 * - متوسط تشابه “التغيرات الموصى بها” (من nextRecommendedMutation + metaCognitionLevel).
 * - نقيسه كسكور بين الأزواج: لو القرار متوافق = 1، مختلف = 0.25؛
 *   ثم نُرجّح بالمتوسط الهندسي لمستويات الميتا.
 */
export function computeMetaResonance(nodes: NodeReport[]): number {
  if (nodes.length <= 1) return 0;
  let acc = 0,
    pairs = 0;

  const dirScore = (a: string, b: string) => {
    if (a === b) return 1;
    // اختلاف بسيط (Expand vs Freeze) أقل من (Expand vs Contract)
    const set = new Set([a, b]);
    if (set.has('Expand') && set.has('Contract')) return 0; // تضاد
    return 0.25; // اختلاف غير متضاد
  };

  for (let i = 0; i < nodes.length; i++) {
    for (let j = i + 1; j < nodes.length; j++) {
      const di = nodes[i].metrics.nextRecommendedMutation;
      const dj = nodes[j].metrics.nextRecommendedMutation;
      const base = dirScore(di, dj);
      const metaGMean = Math.sqrt(
        clamp01(nodes[i].metrics.metaCognitionLevel) * clamp01(nodes[j].metrics.metaCognitionLevel)
      );
      acc += base * metaGMean; // 0..1
      pairs++;
    }
  }
  return pairs > 0 ? clamp01(acc / pairs) : 0;
}

/**
 * القرار الجماعي النهائي:
 * - إن كان collectiveStability مرتفعًا (~>0.8) و metaResonance متوسط/مرتفع:
 *   - لو collaborationIndex أيضًا مرتفع (~>0.7) → "Merge" (تقارب قوي، اندماج سياسات)
 *   - وإلا → "Expand" (توسيع تدريجي)
 * - إن كان metaResonance منخفض (<0.35) → "Freeze" (لتقليل الضوضاء وجمع بيانات)
 * - إن كانت مؤشرات التعاون سلبية (<0.3) مع استقرار منخفض → "Contract"
 */
export function decideDirective(
  collectiveStability: number,
  collaborationIndex: number,
  metaResonance: number
): 'Expand' | 'Contract' | 'Freeze' | 'Merge' {
  const S = collectiveStability;
  const C = collaborationIndex;
  const M = metaResonance;

  if (S > 0.8 && M >= 0.5) {
    if (C >= 0.7) return 'Merge';
    return 'Expand';
  }
  if (M < 0.35) return 'Freeze';
  if (S < 0.45 && C < 0.3) return 'Contract';
  // حالة محايدة — توسّع حذر
  return 'Expand';
}

// =================== Report Builder ===================

export function buildIntegrationReport(nodes: NodeReport[]): IntegrationReport {
  const collectiveStability = clamp01(computeCollectiveStability(nodes));
  const collaborationIndex = clamp01(computeCollaborationIndex(nodes));
  const metaResonance = clamp01(computeMetaResonance(nodes));
  const nextEvolutionDirective = decideDirective(
    collectiveStability,
    collaborationIndex,
    metaResonance
  );

  const timestamp = new Date().toISOString();
  const report: IntegrationReport = {
    timestamp,
    nodes: nodes.map((n) => ({ nodeId: n.nodeId, trust: clamp01(n.trust ?? 0.7) })),
    metrics: {
      collectiveStability,
      collaborationIndex,
      metaResonance,
      nextEvolutionDirective,
    },
    notes: {
      interpretation:
        nextEvolutionDirective === 'Merge'
          ? 'تماهٍ جماعي قوي واستقرار مرتفع — اندماج سياسات مناسب.'
          : nextEvolutionDirective === 'Expand'
          ? 'المجتمع مستقر وجاهز لتوسيع تدريجي مشترك.'
          : nextEvolutionDirective === 'Contract'
          ? 'تباعد عالي مع استقرار ضعيف — الإنكماش لتقليل الضرر.'
          : 'تشويش/اختلاف ميتا مرتفع — التجميد المؤقت لجمع بيانات.',
      guidance:
        'نفّذ تغييرات صغيرة (3%–5%) لكل دورة، لا تُعدّل أكثر من Policy حرجة واحدة لكل عقدة، ' +
        'راقب metaResonance بعد كل تطبيق واقعي، وفعّل آلية مراجعة جماعية للأوزان المتقاربة.',
    },
  };

  return report;
}

// =================== Persistence & Bus Integration ===================

export interface SaveOptions {
  outPath?: string;
  ensureDir?: boolean;
}

export function saveIntegrationReport(rep: IntegrationReport, opts: SaveOptions = {}) {
  const out = path.resolve(opts.outPath ?? './storage/Integration_Report.json');
  if (opts.ensureDir !== false) {
    fs.mkdirSync(path.dirname(out), { recursive: true });
  }
  fs.writeFileSync(out, JSON.stringify(rep, null, 2));
  return out;
}

/**
 * تجميع التقارير من ملفات Evolution_Report.json متعدّدة.
 * تمرير paths لفايلات مختلفة (كل فايل = عقدة) + nodeId لكل واحد.
 */
export function loadNodeReportsFromFiles(
  entries: Array<{ nodeId: string; path: string; trust?: number }>
): NodeReport[] {
  const nodes: NodeReport[] = [];
  for (const e of entries) {
    try {
      const raw = fs.readFileSync(e.path, 'utf8');
      const json = JSON.parse(raw);

      const metrics = json?.metrics ?? {};
      const registrySnapshot = json?.registrySnapshot ?? [];

      nodes.push({
        nodeId: e.nodeId,
        metrics: {
          adaptationRate: Number(metrics.adaptationRate ?? 0),
          stabilityIndex: Number(metrics.stabilityIndex ?? 0),
          metaCognitionLevel: Number(metrics.metaCognitionLevel ?? 0),
          nextRecommendedMutation: metrics.nextRecommendedMutation ?? 'Freeze',
        },
        registrySnapshot,
        sourcePath: e.path,
        trust: e.trust ?? 0.7,
      });
    } catch {
      // تجاهل أي ملف غير صالح — ممكن لاحقاً نسجل Warning
    }
  }
  return nodes;
}

/**
 * تفعيل الاستقبال عبر Bus: أي رسالة CFP من نوع BROADCAST_REPORT
 * تحمل Evolution_Report.json (أو ملخصه) → نضيف/نحدّث NodeReport محلياً ونُصدر Integration_Report جديد.
 */
export class SocialAdaptationEngine {
  private nodes = new Map<string, NodeReport>();
  private lastReport?: IntegrationReport;

  constructor(private opts: { autoSave?: boolean; outPath?: string } = {}) {}

  ingestNodeReport(n: NodeReport) {
    const trust = clamp01(n.trust ?? 0.7);
    const merged: NodeReport = { ...n, trust };
    this.nodes.set(n.nodeId, merged);
  }

  ingestMany(list: NodeReport[]) {
    list.forEach((n) => this.ingestNodeReport(n));
  }

  computeAndStore(): IntegrationReport {
    const all = Array.from(this.nodes.values());
    const rep = buildIntegrationReport(all);
    this.lastReport = rep;
    if (this.opts.autoSave !== false) {
      saveIntegrationReport(rep, { outPath: this.opts.outPath, ensureDir: true });
    }
    return rep;
  }

  getLastReport() {
    return this.lastReport;
  }

  /**
   * يربط المحرك بالباص ليستقبل بث التقارير.
   * نتوقع payload بشكل:
   * { nodeId, report: EvolutionReportLike, trust?, registrySnapshot? }
   */
  bindToBus(bus = Bus) {
    bus.on<CFPMessage>('cfp:*', async (msg) => {
      // الرسائل هنا قد تكون CFPMessage بالفعل — لو اللي بعت بيستخدم Bus.emit("cfp:...", message)
      const maybe = msg as any;
      if (!maybe || typeof maybe !== 'object') return;

      const asCFP = maybe as CFPMessage;
      if (!isReportBroadcast(asCFP)) return;

      const v = validateMessage(asCFP);
      if (!v.valid) return;

      const p = asCFP.payload as any;
      if (!p?.nodeId || !p?.report?.metrics) return;

      const node: NodeReport = {
        nodeId: String(p.nodeId),
        metrics: {
          adaptationRate: Number(p.report.metrics.adaptationRate ?? 0),
          stabilityIndex: Number(p.report.metrics.stabilityIndex ?? 0),
          metaCognitionLevel: Number(p.report.metrics.metaCognitionLevel ?? 0),
          nextRecommendedMutation: p.report.metrics.nextRecommendedMutation ?? 'Freeze',
        },
        registrySnapshot: p.report.registrySnapshot ?? [],
        trust: clamp01(Number(asCFP.trust ?? p.trust ?? 0.7)),
      };

      this.ingestNodeReport(node);
      const rep = this.computeAndStore();

      // الرد على البث برسالة ملخصة (اختياري)
      const reply = buildMessage(
        'SYNC_SNAPSHOT',
        { integration: rep },
        {
          sourceId: 'integration-hub',
          targetId: asCFP.sourceId,
          trust: 0.9,
          meta: { replyTo: asCFP.id, tags: ['auto-reply', 'phase7'] },
        }
      );

      await bus.emit('cfp:SYNC_SNAPSHOT:reply', reply);
    });
  }
}
