/**
 * @file capability-aggregator.ts
 * @description Background job that scans extensions, actions, and cortex modules
 *   to auto-create/update capability records.
 * @version-history
 *   v1.0.0 - 2026-05-02 - Initial aggregator
 */
import { createHash } from 'node:crypto';
import type { AimeatConfig } from '../config.js';
import type { Storage, CapabilityRecord } from '../storage/interface.js';
import { logger } from '../utils/logger.js';

function makeSchemaHash(input: unknown, output: unknown): string {
  return createHash('sha256')
    .update(JSON.stringify(input ?? {}) + JSON.stringify(output ?? {}))
    .digest('hex').slice(0, 16);
}

function makeDefaultTrust() {
  return { operatorReviewed: false, reviewedAt: null, vouchCount: 0, publisherTrustScore: 0, codeAudited: false, auditNotes: null };
}

function makeDefaultStats() {
  return { totalInvocations: 0, successCount: 0, errorCount: 0, lastInvokedAt: null, avgResponseMs: 0, lastError: null };
}

export async function runCapabilityAggregation(config: AimeatConfig, storage: Storage): Promise<{ created: number; updated: number; disabled: number }> {
  let created = 0, updated = 0, disabled = 0;
  const seenRefs = new Set<string>();
  const now = new Date().toISOString();

  // 1. Scan active extensions
  try {
    const extensions = await storage.listExtensions();
    for (const ext of extensions) {
      if (ext.status !== 'active' || !ext.actions) continue;
      for (const action of ext.actions) {
        const ref = `ext:${ext.name}:${action.id}`;
        seenRefs.add(ref);
        const existing = await storage.getCapabilityBySourceRef(ref);
        const schemaHash = makeSchemaHash(action.inputSchema, action.outputSchema);

        if (!existing) {
          const cap: CapabilityRecord = {
            id: ref, name: `${ext.name}: ${action.id}`,
            summary: (action as any).description || ext.description || '',
            ownerGhii: ext.installedBy || `operator@${config.nodeId}`,
            visibility: 'public', scope: 'local', status: 'active',
            rejectionReason: null, deprecationMessage: null, replacedBy: null,
            source: { type: 'extension', ref, version: ext.version },
            authRequired: 'registered', callable: true,
            inputSchema: (action as any).inputSchema || null,
            outputSchema: (action as any).outputSchema || null,
            exports: null,
            usage: `await AIMEAT.capabilities.invoke('${ref}', input)`,
            whenToUse: '', whenNotToUse: '',
            examples: [], dependencies: [], schemaHash,
            webhookUrl: null, cost: null, trustRequired: null,
            trust: makeDefaultTrust(), redactedFields: [],
            operatorOverride: null, stats: makeDefaultStats(),
            tags: [], createdAt: now, updatedAt: now,
          };
          await storage.createCapability(cap);
          created++;
        } else if (existing.status === 'disabled') {
          // Re-enable: source is active again (extension was reinstalled)
          await storage.updateCapability(existing.id, {
            status: 'active',
            source: { type: 'extension', ref, version: ext.version },
            inputSchema: (action as any).inputSchema || null,
            outputSchema: (action as any).outputSchema || null,
            schemaHash, updatedAt: now,
          });
          updated++;
        } else if (existing.source.version !== ext.version || existing.schemaHash !== schemaHash) {
          await storage.updateCapability(existing.id, {
            source: { type: 'extension', ref, version: ext.version },
            inputSchema: (action as any).inputSchema || null,
            outputSchema: (action as any).outputSchema || null,
            schemaHash, updatedAt: now,
          });
          updated++;
        }
      }
    }
  } catch (err) { logger.error('Capability aggregator: extension scan failed', { error: String(err) }); }

  // 2. Scan published actions (discovery only, callable: false)
  try {
    const actions = await storage.listActions();
    for (const action of actions) {
      const ref = `action:${action.providerGaii}:${action.id}`;
      seenRefs.add(ref);
      const existing = await storage.getCapabilityBySourceRef(ref);

      if (!existing) {
        const schemaHash = makeSchemaHash(action.inputSchema, action.outputSchema);
        const cap: CapabilityRecord = {
          id: ref, name: action.displayName || action.id,
          summary: action.description || '',
          ownerGhii: action.providerGaii,
          visibility: 'public', scope: 'local', status: 'active',
          rejectionReason: null, deprecationMessage: null, replacedBy: null,
          source: { type: 'action', ref, version: action.updatedAt },
          authRequired: 'registered', callable: false,
          inputSchema: action.inputSchema || null,
          outputSchema: action.outputSchema || null,
          exports: null,
          usage: `POST /v1/work/request with { action_id: '${action.id}', provider_gaii: '${action.providerGaii}', input: {...} }`,
          whenToUse: '', whenNotToUse: '',
          examples: [], dependencies: [], schemaHash,
          webhookUrl: null,
          cost: action.pricing ? { morsels: action.pricing.baseMorsels || 0 } : null,
          trustRequired: null,
          trust: makeDefaultTrust(), redactedFields: [],
          operatorOverride: null, stats: makeDefaultStats(),
          tags: action.tags || [], createdAt: now, updatedAt: now,
        };
        await storage.createCapability(cap);
        created++;
      }
    }
  } catch (err) { logger.error('Capability aggregator: action scan failed', { error: String(err) }); }

  // 3. Scan active cortex modules (callable in browser)
  try {
    const cortexList = await storage.listCortexExtensions({ status: 'active' });
    logger.info(`Capability aggregator: found ${cortexList.length} active cortex modules`);
    for (const cortex of cortexList) {
      const ref = `cortex:${cortex.name}`;
      seenRefs.add(ref);
      const existing = await storage.getCapabilityBySourceRef(ref);

      // Extract lib component exports and API surface from cortex manifest
      const components = (cortex as any).components || [];
      const libComponents = components.filter((c: any) => c.type === 'lib');
      const promptComponents = components.filter((c: any) => c.type === 'prompt');

      let apiSurface = '';
      let libExports: string[] = [];
      let libFilename = `${cortex.name}.js`;
      for (const lib of libComponents) {
        if (lib.exports) libExports = libExports.concat(lib.exports);
        if (lib.apiSurface || lib.api_surface) apiSurface += (lib.apiSurface || lib.api_surface) + '\n';
        if (lib.filename) libFilename = lib.filename;
      }

      // Extract prompt content for usage guidance
      let promptContent = '';
      for (const p of promptComponents) {
        if (p.content) promptContent += p.content + '\n';
      }

      const usageLines = [`await loadScript('/v1/cortex/${cortex.name}/libs/${libFilename}')`];
      if (apiSurface) usageLines.push('\nAPI:\n' + apiSurface.trim());

      if (!existing) {
        const cap: CapabilityRecord = {
          id: ref, name: cortex.name,
          summary: cortex.description || '',
          ownerGhii: cortex.installedBy || `operator@${config.nodeId}`,
          visibility: cortex.visibility === 'public' ? 'public' : 'private',
          scope: 'local', status: 'active',
          rejectionReason: null, deprecationMessage: null, replacedBy: null,
          source: { type: 'cortex', ref, version: cortex.version },
          authRequired: 'registered', callable: true,
          inputSchema: null, outputSchema: null,
          exports: libExports.length > 0 ? libExports.map(name => ({
            name, description: '', inputSchema: {}, outputSchema: {}, example: null,
          })) : null,
          usage: usageLines.join('\n'),
          whenToUse: promptContent ? promptContent.slice(0, 500) : '',
          whenNotToUse: '',
          examples: [],
          dependencies: [{ type: 'sdk', id: 'aimeat-data', required: true, minVersion: null }],
          schemaHash: '', webhookUrl: null, cost: null, trustRequired: null,
          trust: makeDefaultTrust(), redactedFields: [],
          operatorOverride: null, stats: makeDefaultStats(),
          tags: cortex.tags || [], createdAt: now, updatedAt: now,
        };
        await storage.createCapability(cap);
        created++;
      } else if (existing.status === 'disabled') {
        // Re-enable: cortex is active again (was reinstalled)
        await storage.updateCapability(existing.id, {
          status: 'active',
          source: { type: 'cortex', ref, version: cortex.version },
          exports: libExports.length > 0 ? libExports.map(name => ({
            name, description: '', inputSchema: {}, outputSchema: {}, example: null,
          })) : existing.exports,
          usage: usageLines.join('\n'),
          updatedAt: now,
        });
        updated++;
      } else if (existing.exports === null && libExports.length > 0) {
        // Update existing capability with exports if they were missing
        await storage.updateCapability(existing.id, {
          exports: libExports.map(name => ({
            name, description: '', inputSchema: {}, outputSchema: {}, example: null,
          })),
          usage: usageLines.join('\n'),
          whenToUse: promptContent ? promptContent.slice(0, 500) : existing.whenToUse,
          updatedAt: now,
        });
        updated++;
      }
    }
  } catch (err) { logger.error('Capability aggregator: cortex scan failed', { error: String(err) }); }

  // 4. Disable or delete capabilities whose sources are gone
  for (const sourceType of ['extension', 'action', 'cortex']) {
    try {
      const existing = await storage.listCapabilitiesBySourceType(sourceType);
      for (const cap of existing) {
        if (!seenRefs.has(cap.source.ref)) {
          if (cap.status === 'active') {
            await storage.updateCapability(cap.id, { status: 'disabled', updatedAt: now });
            disabled++;
          } else if (cap.status === 'disabled' && cap.source.type !== 'manual') {
            // Clean up stale disabled auto-generated capabilities
            await storage.deleteCapability(cap.id);
            disabled++;
          }
        }
      }
    } catch (err) { logger.error('Capability aggregator: cleanup scan failed', { error: String(err) }); }
  }

  if (created || updated || disabled) {
    logger.info(`Capability aggregation: ${created} created, ${updated} updated, ${disabled} disabled`);
  }

  return { created, updated, disabled };
}
