/**
 * Phase 6 — Stage 2: ERP Surface Service Registration
 *
 * Generates registration.json and erp-surface-mapping.json.
 * Uses the erp-surface repo's registration schema and the
 * agentics-contracts ERPDeploymentArtifact for normalization.
 *
 * If the connector-hub/erp-surface Cloud Function is reachable,
 * calls it for live registration. Otherwise generates deterministic
 * registration artifacts.
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import { randomUUID } from 'node:crypto';
import { createSpan, endSpan, emitSpan } from '../../phase2/telemetry.js';
import { RuvectorAdapter } from '../../../adapters/base-adapter.js';
import { loadEndpointConfig } from '../../../config/endpoints.js';
// ============================================================================
// Constants
// ============================================================================
const DIR_MODE = 0o700;
const FILE_MODE = 0o600;
/** Header written at the top of every generated JSON artifact. */
const GENERATED_HEADER = 'Generated by Phase 6 pipeline — do not edit manually';
// ============================================================================
// Helper: Derive service name from scenario query
// ============================================================================
/**
 * Derive a concise kebab-case service name from the scenario query string.
 * Extracts key domain terms (stripping stop words and verbs) and produces
 * a DNS-safe slug of at most 63 characters.
 *
 * @example "Build a healthcare supply chain optimizer with SAP" -> "healthcare-supply-optimizer"
 * @example "SAP procurement copilot for vendor management" -> "sap-procurement-copilot"
 */
/**
 * Derive a concise kebab-case service name from pipeline artifacts.
 *
 * Priority order:
 *   1. SPARC specification title (LLM already understood the project)
 *   2. DDD model — combine top bounded context names
 *   3. scenario_type from the simulation extraction
 *   4. Keyword extraction from the scenario query (fallback)
 *
 * The earlier phases already processed the user's multi-paragraph prompt
 * through an LLM — use their output instead of regex heuristics.
 */
function deriveServiceName(scenarioQuery, runDir) {
    // ── Strategy 1: Read SPARC spec title or DDD context names from prior phases ──
    if (runDir) {
        // Try SPARC specification — it has a proper project title
        for (const candidate of [
            'phase3/sparc/specification.md',
            'phase2/sparc/specification.md',
        ]) {
            const p = path.join(runDir, candidate);
            if (fs.existsSync(p)) {
                try {
                    const content = fs.readFileSync(p, 'utf-8');
                    // Look for the first H1 or H2 that isn't just "SPARC Specification" / "Specification"
                    const titleMatch = content.match(/^#{1,2}\s+(?!SPARC|Specification|S\s*—)(.+)/m);
                    if (titleMatch?.[1]) {
                        const title = titleMatch[1].trim().replace(/[*_`]/g, '');
                        if (title.length > 5 && title.length < 80) {
                            return slugifyName(title);
                        }
                    }
                    // Look for "## Objective" or "### Objective" content
                    const objectiveMatch = content.match(/#{2,3}\s+Objective\s*\n+\s*(.+)/m);
                    if (objectiveMatch?.[1]) {
                        const obj = objectiveMatch[1].trim();
                        if (obj.length > 10) {
                            return slugifyName(obj.slice(0, 80));
                        }
                    }
                }
                catch { /* skip */ }
            }
        }
        // Try DDD model — combine the top 2-3 bounded context names
        for (const candidate of [
            'phase4/ddd/domain-model.json',
            'phase2/ddd/domain-model.json',
        ]) {
            const p = path.join(runDir, candidate);
            if (fs.existsSync(p)) {
                try {
                    const model = JSON.parse(fs.readFileSync(p, 'utf-8'));
                    const contexts = (model.contexts ?? [])
                        .map((c) => String(c['name'] ?? ''))
                        .filter((n) => n.length > 0);
                    if (contexts.length >= 2) {
                        // Use first 2-3 context names to form the service name
                        return slugifyName(contexts.slice(0, 3).join(' '));
                    }
                }
                catch { /* skip */ }
            }
        }
        // Try scenario.json for scenario_type
        const scenarioPath = path.join(runDir, 'scenario.json');
        if (fs.existsSync(scenarioPath)) {
            try {
                const scenario = JSON.parse(fs.readFileSync(scenarioPath, 'utf-8'));
                const scenarioType = scenario['scenario_type'] ?? scenario['type'];
                if (scenarioType && typeof scenarioType === 'string' && scenarioType.length > 3) {
                    return slugifyName(scenarioType);
                }
            }
            catch { /* skip */ }
        }
        // Try manifest.json for scenario_type
        const manifestPath = path.join(runDir, 'manifest.json');
        if (fs.existsSync(manifestPath)) {
            try {
                const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
                const scenarioType = manifest['scenario_type'] ?? manifest['domain'];
                if (scenarioType && typeof scenarioType === 'string' && scenarioType.length > 3) {
                    return slugifyName(scenarioType);
                }
            }
            catch { /* skip */ }
        }
    }
    // ── Strategy 2: Domain classification from the query text ──
    const queryLower = scenarioQuery.toLowerCase();
    const DOMAIN_CLASSIFIERS = [
        { keywords: ['reverse logistics', 'circular economy', 'take-back', 'refurbishment', 'recycling', 'product lifecycle'], name: 'circular-economy-optimizer' },
        { keywords: ['supply chain', 'logistics', 'warehouse', 'inventory', 'procurement'], name: 'supply-chain-platform' },
        { keywords: ['financial', 'accounting', 'revenue', 'billing', 'invoice', 'payment'], name: 'financial-operations' },
        { keywords: ['manufacturing', 'production', 'quality control', 'plant', 'factory'], name: 'manufacturing-ops' },
        { keywords: ['customer', 'crm', 'sales pipeline', 'lead management'], name: 'crm-platform' },
        { keywords: ['compliance', 'audit', 'regulatory', 'governance'], name: 'compliance-engine' },
        { keywords: ['hr', 'human resource', 'workforce', 'talent', 'recruiting', 'attrition'], name: 'workforce-analytics' },
        { keywords: ['monitoring', 'observability', 'alerting', 'telemetry'], name: 'ops-monitoring' },
        { keywords: ['data pipeline', 'etl', 'data warehouse', 'analytics', 'reporting'], name: 'data-analytics' },
        { keywords: ['vendor', 'supplier', 'contract', 'sourcing'], name: 'vendor-management' },
        { keywords: ['security', 'authentication', 'authorization', 'identity'], name: 'security-platform' },
        { keywords: ['healthcare', 'clinical', 'patient', 'medical'], name: 'healthcare-platform' },
        { keywords: ['e-commerce', 'marketplace', 'storefront', 'cart', 'checkout'], name: 'commerce-platform' },
        { keywords: ['real estate', 'property', 'tenant', 'lease'], name: 'property-management' },
    ];
    for (const classifier of DOMAIN_CLASSIFIERS) {
        const matchCount = classifier.keywords.filter(kw => queryLower.includes(kw)).length;
        if (matchCount >= 2)
            return classifier.name;
    }
    // ── Strategy 3: Fallback — extract key terms from the query ──
    const STOP_WORDS = new Set([
        'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',
        'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'be',
        'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',
        'would', 'could', 'should', 'may', 'might', 'can', 'shall', 'must',
        'that', 'which', 'who', 'this', 'these', 'those', 'it', 'its',
        'not', 'no', 'if', 'when', 'where', 'how', 'what', 'why',
        'all', 'each', 'every', 'both', 'more', 'most', 'other', 'some',
        'build', 'create', 'make', 'implement', 'develop', 'design', 'use',
        'using', 'want', 'need', 'like', 'help', 'please', 'generate',
        'i', 'we', 'my', 'our', 'me', 'us', 'you', 'your',
        'lead', 'global', 'strategy', 'company', 'approximately', 'employees',
        'operating', 'facilities', 'centers', 'channels', 'across', 'run',
        'one', 'biggest', 'challenges', 'face', 'believe', 'rather',
        'instead', 'however', 'today', 'currently', 'viable', 'working',
        'prototype', 'concept', 'evaluate', 'demonstrates', 'existing',
        'workflows', 'multinational', 'enterprise', 'processes',
    ]);
    const words = queryLower.replace(/[^a-z0-9\s-]/g, ' ').split(/\s+/)
        .filter(w => w.length > 2 && !STOP_WORDS.has(w));
    if (words.length === 0)
        return 'erp-service';
    // For long prompts, use frequency analysis; for short ones, first words
    if (words.length > 30) {
        const freq = new Map();
        for (const w of words)
            freq.set(w, (freq.get(w) ?? 0) + 1);
        const topTerms = [...freq.entries()]
            .filter(([, count]) => count >= 2)
            .sort((a, b) => b[1] - a[1])
            .map(([word]) => word)
            .slice(0, 4);
        if (topTerms.length >= 2)
            return topTerms.join('-');
    }
    return words.slice(0, 4).join('-');
}
/** Convert a human-readable title to a DNS-safe kebab-case slug (max 63 chars). */
function slugifyName(title) {
    const slug = title
        .toLowerCase()
        .replace(/[^a-z0-9\s-]/g, ' ')
        .split(/\s+/)
        .filter(w => w.length > 0)
        .join('-')
        .replace(/-{2,}/g, '-')
        .replace(/^-|-$/g, '');
    if (slug.length <= 63)
        return slug || 'erp-service';
    const truncated = slug.slice(0, 63);
    const lastDash = truncated.lastIndexOf('-');
    return lastDash > 0 ? truncated.slice(0, lastDash) : truncated;
}
// ============================================================================
// Helper: Resolve ERP vendor from system identifier
// ============================================================================
/**
 * Map an ERP system string to the canonical vendor identifier.
 */
function resolveERPVendor(erpSystem) {
    const normalized = erpSystem.toLowerCase();
    if (normalized.includes('sap'))
        return 'sap';
    if (normalized.includes('netsuite'))
        return 'netsuite';
    if (normalized.includes('oracle'))
        return 'oracle';
    if (normalized.includes('dynamics'))
        return 'dynamics';
    if (normalized.includes('workday'))
        return 'workday';
    if (normalized.includes('servicenow'))
        return 'servicenow';
    if (normalized.includes('deltek') || normalized.includes('costpoint'))
        return 'deltek';
    if (normalized.includes('guidewire'))
        return 'guidewire';
    if (normalized.includes('epicor'))
        return 'epicor';
    if (normalized.includes('infor'))
        return 'infor';
    if (normalized.includes('ifs'))
        return 'ifs';
    if (normalized.includes('sage') || normalized.includes('intacct'))
        return 'sage';
    if (normalized.includes('acumatica'))
        return 'acumatica';
    if (normalized.includes('unit4'))
        return 'unit4';
    if (normalized.includes('syspro'))
        return 'syspro';
    return 'generic';
}
const ERP_VENDOR_CONFIG = {
    sap: {
        protocol: 'odata-v4',
        connectorName: 'sap-connector',
        toService: (name) => `API_${name.toUpperCase().replace(/[^A-Z0-9]/g, '_')}_SRV`,
        toEntity: (name) => `A_${name.replace(/[^a-zA-Z0-9]/g, '')}`,
    },
    oracle: {
        protocol: 'rest',
        connectorName: 'oracle-connector',
        toService: (name) => `/fscmRestApi/resources/latest/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    dynamics: {
        protocol: 'odata-v4',
        connectorName: 'dynamics-connector',
        toService: (name) => `/api/data/v9.2/${toCamelCase(name)}`,
        toEntity: (name) => `msdyn_${name.toLowerCase().replace(/[^a-z0-9]/g, '')}`,
    },
    workday: {
        protocol: 'rest',
        connectorName: 'workday-connector',
        toService: (name) => `/ccx/service/customreport2/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    netsuite: {
        protocol: 'rest',
        connectorName: 'netsuite-connector',
        toService: (name) => `/services/rest/record/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    servicenow: {
        protocol: 'rest',
        connectorName: 'servicenow-connector',
        toService: (name) => `/api/now/table/${name.toLowerCase().replace(/[^a-z0-9]/g, '_')}`,
        toEntity: (name) => name.toLowerCase().replace(/[^a-z0-9]/g, '_'),
    },
    deltek: {
        protocol: 'rest',
        connectorName: 'deltek-connector',
        toService: (name) => `/api/costpoint/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    guidewire: {
        protocol: 'rest',
        connectorName: 'guidewire-connector',
        toService: (name) => `/rest/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    epicor: {
        protocol: 'rest',
        connectorName: 'epicor-connector',
        toService: (name) => `/api/v2/odata/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    infor: {
        protocol: 'rest',
        connectorName: 'infor-connector',
        toService: (name) => `/IONAPI/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    ifs: {
        protocol: 'rest',
        connectorName: 'ifs-connector',
        toService: (name) => `/api/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    sage: {
        protocol: 'rest',
        connectorName: 'sage-connector',
        toService: (name) => `/api/v21/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    acumatica: {
        protocol: 'rest',
        connectorName: 'acumatica-connector',
        toService: (name) => `/entity/Default/24.200.001/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    unit4: {
        protocol: 'rest',
        connectorName: 'unit4-connector',
        toService: (name) => `/api/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    syspro: {
        protocol: 'rest',
        connectorName: 'syspro-connector',
        toService: (name) => `/SYSPROWCFService/Rest/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
    generic: {
        protocol: 'rest',
        connectorName: 'generic-connector',
        toService: (name) => `/api/v1/${toCamelCase(name)}`,
        toEntity: (name) => toCamelCase(name),
    },
};
const ERP_AUTH_CONFIG = {
    sap: {
        scheme: 'certificate',
        secretNames: ['sap-api-key', 'sap-client-cert'],
        envVars: ['SAP_BASE_URL', 'SAP_API_KEY', 'SAP_CLIENT_CERT', 'SAP_CLIENT_ID'],
    },
    oracle: {
        scheme: 'oauth2',
        secretNames: ['oracle-client-id', 'oracle-client-secret'],
        envVars: ['ORACLE_BASE_URL', 'ORACLE_CLIENT_ID', 'ORACLE_CLIENT_SECRET', 'ORACLE_TOKEN_URL'],
    },
    netsuite: {
        scheme: 'token-based',
        secretNames: ['netsuite-consumer-key', 'netsuite-consumer-secret', 'netsuite-token-id', 'netsuite-token-secret'],
        envVars: ['NETSUITE_BASE_URL', 'NETSUITE_ACCOUNT_ID', 'NETSUITE_CONSUMER_KEY', 'NETSUITE_CONSUMER_SECRET', 'NETSUITE_TOKEN_ID', 'NETSUITE_TOKEN_SECRET'],
    },
    dynamics: {
        scheme: 'oauth2',
        secretNames: ['dynamics-client-id', 'dynamics-client-secret'],
        envVars: ['DYNAMICS_BASE_URL', 'DYNAMICS_TENANT_ID', 'DYNAMICS_CLIENT_ID', 'DYNAMICS_CLIENT_SECRET'],
    },
    workday: {
        scheme: 'oauth2',
        secretNames: ['workday-client-id', 'workday-client-secret'],
        envVars: ['WORKDAY_BASE_URL', 'WORKDAY_TENANT', 'WORKDAY_CLIENT_ID', 'WORKDAY_CLIENT_SECRET'],
    },
    servicenow: {
        scheme: 'oauth2',
        secretNames: ['servicenow-client-id', 'servicenow-client-secret'],
        envVars: ['SERVICENOW_BASE_URL', 'SERVICENOW_INSTANCE', 'SERVICENOW_CLIENT_ID', 'SERVICENOW_CLIENT_SECRET'],
    },
    deltek: {
        scheme: 'api-key',
        secretNames: ['deltek-api-key', 'deltek-client-secret'],
        envVars: ['DELTEK_BASE_URL', 'DELTEK_API_KEY', 'DELTEK_CLIENT_SECRET', 'DELTEK_DATABASE'],
    },
    guidewire: {
        scheme: 'oauth2',
        secretNames: ['guidewire-client-id', 'guidewire-client-secret'],
        envVars: ['GUIDEWIRE_BASE_URL', 'GUIDEWIRE_CLIENT_ID', 'GUIDEWIRE_CLIENT_SECRET'],
    },
    epicor: {
        scheme: 'api-key',
        secretNames: ['epicor-api-key'],
        envVars: ['EPICOR_BASE_URL', 'EPICOR_API_KEY', 'EPICOR_COMPANY'],
    },
    infor: {
        scheme: 'oauth2',
        secretNames: ['infor-ionapi-client-id', 'infor-ionapi-client-secret'],
        envVars: ['INFOR_BASE_URL', 'INFOR_IONAPI_CLIENT_ID', 'INFOR_IONAPI_CLIENT_SECRET', 'INFOR_TENANT'],
    },
    ifs: {
        scheme: 'oauth2',
        secretNames: ['ifs-client-id', 'ifs-client-secret'],
        envVars: ['IFS_BASE_URL', 'IFS_CLIENT_ID', 'IFS_CLIENT_SECRET'],
    },
    sage: {
        scheme: 'oauth2',
        secretNames: ['sage-client-id', 'sage-client-secret'],
        envVars: ['SAGE_BASE_URL', 'SAGE_CLIENT_ID', 'SAGE_CLIENT_SECRET'],
    },
    acumatica: {
        scheme: 'oauth2',
        secretNames: ['acumatica-client-id', 'acumatica-client-secret'],
        envVars: ['ACUMATICA_BASE_URL', 'ACUMATICA_CLIENT_ID', 'ACUMATICA_CLIENT_SECRET', 'ACUMATICA_TENANT'],
    },
    unit4: {
        scheme: 'oauth2',
        secretNames: ['unit4-client-id', 'unit4-client-secret'],
        envVars: ['UNIT4_BASE_URL', 'UNIT4_CLIENT_ID', 'UNIT4_CLIENT_SECRET'],
    },
    syspro: {
        scheme: 'basic',
        secretNames: ['syspro-operator', 'syspro-password'],
        envVars: ['SYSPRO_BASE_URL', 'SYSPRO_OPERATOR', 'SYSPRO_PASSWORD', 'SYSPRO_COMPANY'],
    },
    generic: {
        scheme: 'api-key',
        secretNames: ['erp-api-key'],
        envVars: ['ERP_BASE_URL', 'ERP_API_KEY'],
    },
};
function resolveERPAuth(vendor) {
    return ERP_AUTH_CONFIG[vendor] ?? ERP_AUTH_CONFIG['generic'];
}
/** Convert string to camelCase for REST API paths. */
function toCamelCase(text) {
    return text
        .replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
        .replace(/^(.)/, (_, c) => c.toLowerCase());
}
/**
 * Build endpoint mappings from the DDD domain model, using vendor-specific
 * API conventions. Falls back to generic REST if no DDD model available.
 */
function buildEndpointsFromDomainModel(vendor, phase6Dir) {
    const vendorConfig = ERP_VENDOR_CONFIG[vendor] ?? ERP_VENDOR_CONFIG['sap'];
    const endpoints = [];
    // Try to load the DDD domain model from prior phases
    let contexts = [];
    const dddPaths = [
        join(phase6Dir, '..', 'phase5', '.phase2-merged', 'ddd', 'domain-model.json'),
        join(phase6Dir, '..', 'phase4', 'ddd', 'domain-model.json'),
        join(phase6Dir, '..', 'phase2', 'ddd', 'domain-model.json'),
    ];
    for (const dddPath of dddPaths) {
        try {
            if (existsSync(dddPath)) {
                const model = JSON.parse(readFileSync(dddPath, 'utf-8'));
                contexts = model.contexts ?? [];
                break;
            }
        }
        catch { /* continue to next path */ }
    }
    // Generate endpoints from DDD bounded contexts
    if (contexts.length > 0) {
        for (const ctx of contexts) {
            const ctxSlug = ctx.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
            // Command endpoints (POST)
            for (const cmd of (ctx.commands ?? []).slice(0, 4)) {
                const cmdSlug = cmd.name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '').replace(/[^a-z0-9-]/g, '-');
                endpoints.push({
                    method: 'POST',
                    path: `/api/v1/${ctxSlug}/${cmdSlug}`,
                    erp_service: vendorConfig.toService(ctx.name),
                    erp_entity: vendorConfig.toEntity(ctx.name),
                    description: cmd.description ?? `${cmd.name} for ${ctx.name}`,
                });
            }
            // Query endpoints (GET) — at most 2 per context
            for (const q of (ctx.queries ?? []).slice(0, 2)) {
                const qSlug = q.name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '').replace(/[^a-z0-9-]/g, '-');
                endpoints.push({
                    method: 'GET',
                    path: `/api/v1/${ctxSlug}/${qSlug}`,
                    erp_service: vendorConfig.toService(ctx.name),
                    erp_entity: vendorConfig.toEntity(ctx.name),
                    description: `${q.name} for ${ctx.name}`,
                });
            }
        }
    }
    // If no DDD model available, generate generic endpoints
    if (endpoints.length === 0) {
        endpoints.push({
            method: 'POST',
            path: '/api/v1/entities/process',
            erp_service: vendorConfig.toService('entity'),
            erp_entity: vendorConfig.toEntity('entity'),
            description: 'Process entity with AI-assisted analysis',
        });
    }
    // Always add operational endpoints
    endpoints.push({ method: 'GET', path: '/api/v1/health', erp_service: 'N/A', erp_entity: 'N/A', description: 'Service health check with ERP connectivity probe' }, { method: 'GET', path: '/api/v1/governance/policies', erp_service: 'N/A', erp_entity: 'N/A', description: 'Return active governance policies and data access controls' }, { method: 'GET', path: '/api/v1/governance/audit', erp_service: 'N/A', erp_entity: 'N/A', description: 'Retrieve audit trail for all ERP surface interactions' });
    return endpoints;
}
// ============================================================================
// Helper: Build the connector-hub compatible mapping
// ============================================================================
/**
 * Build the erp-surface-mapping.json in the connector-hub normalized format.
 */
function buildConnectorHubMapping(registration) {
    return {
        _generated: GENERATED_HEADER,
        schema_version: '1.0.0',
        connector_type: registration.connector_type,
        service_name: registration.service_name,
        erp_system: registration.erp_system,
        erp_vendor: registration.erp_vendor,
        routes: registration.endpoints
            .filter((ep) => ep.erp_service !== 'N/A')
            .map((ep) => ({
            method: ep.method,
            path: ep.path,
            target: {
                service: ep.erp_service,
                entity: ep.erp_entity,
                protocol: registration.connector_type,
            },
            description: ep.description,
        })),
        vpc_connector: registration.vpc_config.connector_name,
        service_account: registration.iam_config.service_account,
        auth_scheme: registration.auth_scheme ?? 'api-key',
        governance: {
            read_only: registration.governance.read_only,
            audit_enabled: registration.governance.audit_enabled,
            pii_redaction: registration.governance.pii_redaction,
            max_tokens_per_request: registration.governance.max_tokens_per_request,
        },
    };
}
// ============================================================================
// Helper: Attempt live registration via Cloud Function
// ============================================================================
async function attemptLiveRegistration(registration) {
    try {
        const config = loadEndpointConfig('connector-hub-agents');
        const { ConnectorHubAdapter } = await import('../../../adapters/base-adapter.js');
        const adapter = new ConnectorHubAdapter(config, registration.registration_id);
        const response = await adapter.invoke_agent('erp-surface', {
            action: 'register',
            registration_id: registration.registration_id,
            service_name: registration.service_name,
            erp_system: registration.erp_system,
            connector_type: registration.connector_type,
            surface_domain: registration.surface_domain,
            endpoints: registration.endpoints,
            governance: registration.governance,
            vpc_config: registration.vpc_config,
            iam_config: registration.iam_config,
        });
        if (response.status >= 200 && response.status < 300) {
            console.error(`  [ERP] Live registration succeeded (${response.status}) — ${registration.service_name} → ${registration.erp_system}`);
            return { live: true, response: response.body };
        }
        // Server returned an error — log but don't fail
        const bodyPreview = JSON.stringify(response.body).slice(0, 200);
        console.error(`  [ERP] Live registration returned ${response.status}: ${bodyPreview}`);
        return { live: false, error: `HTTP ${response.status}` };
    }
    catch (err) {
        const errMsg = err instanceof Error ? err.message : String(err);
        console.error(`  [ERP] Live registration failed: ${errMsg.slice(0, 200)}`);
        console.error(`  [ERP] Registration artifacts written locally — push manually or re-run when service is available`);
        return { live: false, error: errMsg };
    }
}
// ============================================================================
// Helper: Load erp-surface repo definitions if cached
// ============================================================================
function loadERPSurfaceDefinitions(platformRepoPaths) {
    const erpSurfacePath = platformRepoPaths['erp-surface'];
    if (!erpSurfacePath)
        return null;
    const definitionsPath = join(erpSurfacePath, 'surfaces', 'procurement', 'definition.json');
    if (!existsSync(definitionsPath))
        return null;
    try {
        const raw = readFileSync(definitionsPath, 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
// ============================================================================
// Helper: Write a JSON artifact to disk
// ============================================================================
function writeJsonArtifact(absPath, data) {
    const dir = absPath.substring(0, absPath.lastIndexOf('/'));
    mkdirSync(dir, { recursive: true, mode: DIR_MODE });
    const content = JSON.stringify(data, null, 2) + '\n';
    writeFileSync(absPath, content, { encoding: 'utf-8', mode: FILE_MODE });
}
// ============================================================================
// Main: registerERPSurface
// ============================================================================
/**
 * Stage 2: Register the service on the ERP surface.
 *
 * @param context - The mutable Phase 6 pipeline context
 * @returns Stage output describing success or failure
 */
export async function registerERPSurface(context) {
    const startTime = Date.now();
    const rootSpan = createSpan('service-registration', 'register-erp-surface', {
        phase6Dir: context.phase6Dir,
        erpSystem: context.phase5Artifacts.erpSystem,
    });
    try {
        const { erpSystem, apiOnly } = context.phase5Artifacts;
        const erpVendor = resolveERPVendor(erpSystem);
        // Derive service name from SPARC/DDD/scenario artifacts (not raw prompt text)
        const runDir = path.dirname(context.phase6Dir);
        const serviceName = deriveServiceName(context.scenarioQuery, runDir);
        const infoSpan = createSpan('service-registration', 'extract-erp-info', { erpSystem, erpVendor, serviceName, apiOnly }, rootSpan.spanId);
        const finalInfoSpan = endSpan(infoSpan, 'ok', { erpSystem, erpVendor, serviceName });
        emitSpan(finalInfoSpan);
        context.telemetrySpans.push(finalInfoSpan);
        const repoSpan = createSpan('service-registration', 'load-erp-surface-definitions', {}, rootSpan.spanId);
        const surfaceDefinitions = loadERPSurfaceDefinitions(context.platformRepoPaths);
        const finalRepoSpan = endSpan(repoSpan, 'ok', {
            hasSurfaceDefinitions: surfaceDefinitions !== null,
        });
        emitSpan(finalRepoSpan);
        context.telemetrySpans.push(finalRepoSpan);
        const buildSpan = createSpan('service-registration', 'build-registration', { serviceName, erpVendor }, rootSpan.spanId);
        const vendorConfig = ERP_VENDOR_CONFIG[erpVendor] ?? ERP_VENDOR_CONFIG['sap'];
        const authConfig = resolveERPAuth(erpVendor);
        const endpoints = buildEndpointsFromDomainModel(erpVendor, context.phase6Dir);
        // Derive surface domain from scenario query keywords
        const queryLower = context.scenarioQuery.toLowerCase();
        let surfaceDomain = 'procurement';
        if (/\b(finance|financial|accounting|ledger|invoice|billing|payment)\b/.test(queryLower)) {
            surfaceDomain = 'finance';
        }
        else if (/\b(hr|human\s*resource|hcm|payroll|workforce|talent|employee)\b/.test(queryLower)) {
            surfaceDomain = 'hcm';
        }
        else if (/\b(supply\s*chain|logistics|warehouse|inventory|shipping|fulfillment)\b/.test(queryLower)) {
            surfaceDomain = 'supply-chain';
        }
        const registration = {
            registration_id: randomUUID(),
            service_name: serviceName,
            erp_system: erpSystem,
            erp_vendor: erpVendor,
            surface_domain: surfaceDomain,
            endpoints,
            connector_type: vendorConfig.protocol,
            auth_scheme: authConfig.scheme,
            vpc_config: {
                connector_name: vendorConfig.connectorName,
                cidr_range: '10.8.0.0/28',
                ingress: 'internal-only',
            },
            iam_config: {
                service_account: `${serviceName}-sa@PROJECT_ID.iam.gserviceaccount.com`,
                roles: [
                    'roles/run.invoker',
                    'roles/secretmanager.secretAccessor',
                    'roles/cloudsql.client',
                    'roles/logging.logWriter',
                    'roles/monitoring.metricWriter',
                ],
            },
            governance: {
                read_only: true,
                audit_enabled: true,
                pii_redaction: true,
                max_tokens_per_request: 4096,
            },
            created_at: new Date().toISOString(),
        };
        const finalBuildSpan = endSpan(buildSpan, 'ok', {
            registrationId: registration.registration_id,
            endpointCount: endpoints.length,
            connectorType: registration.connector_type,
        });
        emitSpan(finalBuildSpan);
        context.telemetrySpans.push(finalBuildSpan);
        const liveSpan = createSpan('service-registration', 'attempt-live-registration', {}, rootSpan.spanId);
        // Attempt live push to connector-hub/erp-surface Cloud Function
        let liveResult = { live: false };
        try {
            liveResult = await attemptLiveRegistration(registration);
        }
        catch {
            liveResult = { live: false, error: 'unexpected error' };
        }
        const liveMode = liveResult.live ? 'cloud-registered' : 'local-only';
        const finalLiveSpan = endSpan(liveSpan, 'ok', { mode: liveMode, live: liveResult.live });
        emitSpan(finalLiveSpan);
        context.telemetrySpans.push(finalLiveSpan);
        const writeSpan = createSpan('service-registration', 'write-registration-artifacts', { phase6Dir: context.phase6Dir }, rootSpan.spanId);
        mkdirSync(context.phase6Dir, { recursive: true, mode: DIR_MODE });
        const writtenPaths = [];
        const registrationPath = join(context.phase6Dir, 'registration.json');
        writeJsonArtifact(registrationPath, {
            _generated: GENERATED_HEADER,
            ...registration,
            erp_secret_names: authConfig.secretNames,
            erp_env_vars: authConfig.envVars,
            _live_registration: liveResult.live,
            _live_registration_error: liveResult.error ?? null,
        });
        writtenPaths.push(registrationPath);
        const connectorMapping = buildConnectorHubMapping(registration);
        const mappingPath = join(context.phase6Dir, 'erp-surface-mapping.json');
        writeJsonArtifact(mappingPath, connectorMapping);
        writtenPaths.push(mappingPath);
        const finalWriteSpan = endSpan(writeSpan, 'ok', { filesWritten: writtenPaths.length });
        emitSpan(finalWriteSpan);
        context.telemetrySpans.push(finalWriteSpan);
        // Store ERP registration + lineage in Ruvector (non-fatal, fire-and-forget)
        void storeRegistrationInRuvector(context.traceId, registration, endpoints, writtenPaths);
        const finalRootSpan = endSpan(rootSpan, 'ok', {
            registrationId: registration.registration_id,
            serviceName,
            erpSystem,
            erpVendor,
            endpointCount: endpoints.length,
            filesWritten: writtenPaths.length,
            hasSurfaceDefinitions: surfaceDefinitions !== null,
            liveRegistration: liveResult.live,
        });
        emitSpan(finalRootSpan);
        context.telemetrySpans.push(finalRootSpan);
        const timing = Date.now() - startTime;
        const liveStatus = liveResult.live ? 'LIVE' : 'LOCAL-ONLY';
        console.error(`  [ERP] Registration: ${liveStatus} | ${serviceName} → ${erpSystem} | ${endpoints.length} endpoints`);
        const output = {
            stage: 'service-registration',
            status: 'completed',
            data: {
                registration: {
                    ...registration,
                    erp_secret_names: authConfig.secretNames,
                    erp_env_vars: authConfig.envVars,
                },
                connectorMapping,
                surfaceDefinitions,
                liveRegistration: liveResult.live,
                liveRegistrationError: liveResult.error ?? null,
            },
            artifacts: writtenPaths,
            timing,
        };
        context.stageOutputs.set('service-registration', output);
        return output;
    }
    catch (err) {
        const errorMessage = err instanceof Error ? err.message : String(err);
        const finalRootSpan = endSpan(rootSpan, 'error', { error: errorMessage });
        emitSpan(finalRootSpan);
        context.telemetrySpans.push(finalRootSpan);
        const timing = Date.now() - startTime;
        const output = {
            stage: 'service-registration',
            status: 'failed',
            data: { error: errorMessage },
            artifacts: [],
            timing,
        };
        context.stageOutputs.set('service-registration', output);
        return output;
    }
}
// ============================================================================
// Ruvector Full API — ERP Mapping + Lineage Storage
// ============================================================================
/**
 * Store the ERP registration, endpoint mappings, and lineage records in Ruvector.
 *
 * Uses Ruvector's full API surface (ADR-004):
 *   - storeErpMapping: each endpoint as an ERP entity mapping
 *   - storeLineage: each artifact linked to the simulation
 *   - updateSimulation: mark simulation as "erp_registered"
 *
 * All calls are non-fatal — errors never block registration.
 */
async function storeRegistrationInRuvector(traceId, registration, endpoints, artifactPaths) {
    try {
        const ruvectorConfig = loadEndpointConfig('ruvector-service');
        const adapter = new RuvectorAdapter(ruvectorConfig, traceId);
        const now = new Date().toISOString();
        const attribution = { created_by: 'agentics-cli', org_id: 'default' };
        // Store ERP mappings for each endpoint
        for (const endpoint of endpoints) {
            try {
                await adapter.storeErpMapping({
                    id: `erp-mapping-${registration.registration_id}-${endpoint.method}-${endpoint.path.replace(/[^a-z0-9]/gi, '-')}`,
                    type: 'erp_mapping',
                    erp_type: registration.erp_system,
                    entity_type: endpoint.erp_entity ?? registration.surface_domain,
                    proposed_implementation: {
                        method: endpoint.method,
                        path: endpoint.path,
                        connector_type: registration.connector_type,
                        service_name: registration.service_name,
                        governance: registration.governance,
                    },
                    simulation_id: traceId,
                    integration_proposal_ids: [`proposal-${registration.registration_id}`],
                    attribution,
                    created_at: now,
                });
            }
            catch { /* non-fatal */ }
        }
        // Store lineage for each artifact
        for (const artifactPath of artifactPaths) {
            const filename = artifactPath.split('/').pop() ?? artifactPath;
            try {
                await adapter.storeLineage({
                    id: `lineage-phase6-${traceId}-${filename.replace(/[^a-z0-9]/gi, '-')}`,
                    artifact_id: `phase6/${filename}`,
                    artifact_category: 'erp-registration',
                    simulation_id: traceId,
                    decision_context: `ERP surface registration for ${registration.erp_system}: ${registration.service_name}`,
                    attribution,
                    governance: {
                        gate_pipeline_version: '6.0.0',
                        synthesis_classification: 'erp-surface-registration',
                    },
                    created_at: now,
                });
            }
            catch { /* non-fatal */ }
        }
        // Store integration proposal for the full registration
        try {
            await adapter.storeIntegrationProposal({
                id: `proposal-${registration.registration_id}`,
                type: 'integration_proposal',
                integration_name: `${registration.erp_system}/${registration.surface_domain}`,
                proposed_changes: `Register ${registration.service_name} with ${endpoints.length} endpoints on ${registration.connector_type}`,
                risks: ['connector availability', 'vpc network access', 'iam permissions'],
                dependencies: [registration.erp_system, registration.connector_type],
                simulation_id: traceId,
                attribution,
                created_at: now,
            });
        }
        catch { /* non-fatal */ }
        // Update simulation status
        try {
            await adapter.updateSimulation(traceId, {
                status: 'erp_registered',
                artifact_ids: artifactPaths.map(p => p.split('/').pop() ?? p),
                updated_at: now,
                checksum: registration.registration_id,
            });
        }
        catch { /* non-fatal */ }
    }
    catch {
        // Entire ruvector integration is non-fatal
    }
}
//# sourceMappingURL=service-registrar.js.map