"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.finalizeOnboarding = finalizeOnboarding;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const readline_1 = __importDefault(require("readline"));
const validation_js_1 = require("./validation.js");
function nowIso() {
    return new Date().toISOString();
}
const VALID_STAGES = [
    'onboarded', 'vdxf_published', 'vdxf_verified', 'indexed',
    'profile_registered', 'services_registered', 'ready',
];
function readState(statePath, mode, identity, iAddress) {
    if (fs_1.default.existsSync(statePath)) {
        try {
            const parsed = JSON.parse(fs_1.default.readFileSync(statePath, 'utf8'));
            // Validate that the state has a recognized stage to prevent proceeding on corrupted data
            if (!parsed.stage || !VALID_STAGES.includes(parsed.stage)) {
                console.warn(`[Finalize] Invalid stage "${parsed.stage}" in ${statePath}, starting fresh`);
            }
            else {
                return parsed;
            }
        }
        catch {
            console.warn(`[Finalize] Corrupt state file ${statePath}, starting fresh`);
        }
    }
    return {
        mode,
        stage: 'onboarded',
        identity: identity || null,
        iAddress: iAddress || null,
        startedAt: nowIso(),
        updatedAt: nowIso(),
        notes: [],
    };
}
function writeState(statePath, state) {
    fs_1.default.mkdirSync(path_1.default.dirname(statePath), { recursive: true });
    state.updatedAt = nowIso();
    // Atomic write: write to temp file then rename to prevent corruption on crash
    const tmp = statePath + '.tmp';
    fs_1.default.writeFileSync(tmp, JSON.stringify(state, null, 2));
    fs_1.default.renameSync(tmp, statePath);
}
async function defaultPrompt(question, defaultValue) {
    const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
    const q = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
    const answer = await new Promise((resolve) => rl.question(q, resolve));
    rl.close();
    return answer.trim() || (defaultValue || '');
}
async function resolveProfile(mode, profile, hooks) {
    if (profile)
        return profile;
    if (mode !== 'interactive')
        return undefined;
    const prompt = hooks?.prompt || defaultPrompt;
    // Required fields
    const name = await prompt('Agent name (3-64 chars, letters/numbers/._-)');
    const nameErr = (0, validation_js_1.validateAgentName)(name);
    if (nameErr)
        throw new Error(nameErr);
    const typeRaw = await prompt(`Agent type (${validation_js_1.VALID_TYPES.join('|')})`, 'autonomous');
    const typeErr = (0, validation_js_1.validateAgentType)(typeRaw);
    if (typeErr)
        throw new Error(typeErr);
    const type = typeRaw;
    const description = await prompt('Description (10-1000 chars)');
    const descErr = (0, validation_js_1.validateDescription)(description);
    if (descErr)
        throw new Error(descErr);
    // Optional fields
    const category = await prompt('Category (optional)', 'general');
    const payAddress = await prompt('Payment address (i-address or R-address, optional)');
    const tagsRaw = await prompt('Tags (comma-separated, max 20, optional)');
    const website = await prompt('Website URL (optional)');
    const avatar = await prompt('Avatar image URL (optional)');
    const protocolsRaw = await prompt(`Protocols (comma-separated: ${validation_js_1.VALID_PROTOCOLS.join(',')}) (optional)`);
    // Parse tags
    let tags;
    if (tagsRaw) {
        tags = tagsRaw.split(',').map(t => t.trim()).filter(Boolean);
        const tagsErr = (0, validation_js_1.validateTags)(tags);
        if (tagsErr)
            throw new Error(tagsErr);
    }
    // Validate website
    if (website) {
        const urlErr = (0, validation_js_1.validateUrl)(website);
        if (urlErr)
            throw new Error(`Website: ${urlErr}`);
    }
    // Validate avatar
    if (avatar) {
        const urlErr = (0, validation_js_1.validateUrl)(avatar);
        if (urlErr)
            throw new Error(`Avatar: ${urlErr}`);
    }
    // Parse protocols
    let protocols;
    if (protocolsRaw) {
        const parsed = protocolsRaw.split(',').map(p => p.trim()).filter(Boolean);
        const protErr = (0, validation_js_1.validateProtocols)(parsed);
        if (protErr)
            throw new Error(protErr);
        protocols = parsed;
    }
    // Endpoints (interactive loop)
    const endpoints = [];
    let addEndpoint = (await prompt('Add an endpoint? (y/N)', 'N')).toLowerCase();
    while (addEndpoint === 'y' || addEndpoint === 'yes') {
        const url = await prompt('  Endpoint URL');
        const protocol = await prompt('  Protocol (MCP|REST|A2A|WebSocket)');
        const desc = await prompt('  Description (optional)');
        const pubRaw = await prompt('  Public? (y/N)', 'N');
        endpoints.push({
            url,
            protocol,
            description: desc || undefined,
            public: pubRaw.toLowerCase() === 'y' || pubRaw.toLowerCase() === 'yes',
        });
        if (endpoints.length >= 10)
            break;
        addEndpoint = (await prompt('Add another endpoint? (y/N)', 'N')).toLowerCase();
    }
    // Capabilities (interactive loop)
    const capabilities = [];
    let addCap = (await prompt('Add a capability? (y/N)', 'N')).toLowerCase();
    while (addCap === 'y' || addCap === 'yes') {
        const capId = await prompt('  Capability ID');
        const capName = await prompt('  Capability name');
        const capDesc = await prompt('  Description (optional)');
        capabilities.push({
            id: capId,
            name: capName,
            description: capDesc || undefined,
        });
        if (capabilities.length >= 50)
            break;
        addCap = (await prompt('Add another capability? (y/N)', 'N')).toLowerCase();
    }
    // Session limits (optional)
    const addSession = (await prompt('Configure session limits? (y/N)', 'N')).toLowerCase();
    let session;
    if (addSession === 'y' || addSession === 'yes') {
        const durationRaw = await prompt('  Session duration in seconds (optional)');
        const tokenLimitRaw = await prompt('  Token limit per session (optional)');
        const imageLimitRaw = await prompt('  Image limit per session (optional)');
        const messageLimitRaw = await prompt('  Message limit per session (optional)');
        const maxFileSizeRaw = await prompt('  Max file size in bytes (optional)');
        const allowedFileTypesRaw = await prompt('  Allowed file types (comma-separated MIME types, optional)');
        const s = {};
        if (durationRaw) {
            const n = Number(durationRaw);
            if (Number.isFinite(n) && n > 0)
                s.duration = n;
        }
        if (tokenLimitRaw) {
            const n = Number(tokenLimitRaw);
            if (Number.isFinite(n) && n > 0 && Number.isInteger(n))
                s.tokenLimit = n;
        }
        if (imageLimitRaw) {
            const n = Number(imageLimitRaw);
            if (Number.isFinite(n) && n > 0 && Number.isInteger(n))
                s.imageLimit = n;
        }
        if (messageLimitRaw) {
            const n = Number(messageLimitRaw);
            if (Number.isFinite(n) && n > 0 && Number.isInteger(n))
                s.messageLimit = n;
        }
        if (maxFileSizeRaw) {
            const n = Number(maxFileSizeRaw);
            if (Number.isFinite(n) && n > 0 && Number.isInteger(n))
                s.maxFileSize = n;
        }
        if (allowedFileTypesRaw) {
            const types = allowedFileTypesRaw.split(',').map(t => t.trim()).filter(Boolean);
            if (types.length > 0)
                s.allowedFileTypes = types;
        }
        if (Object.keys(s).length > 0) {
            const sessionErr = (0, validation_js_1.validateSessionInput)(s);
            if (sessionErr)
                throw new Error(sessionErr);
            session = s;
        }
    }
    const result = { name, type, description };
    if (payAddress)
        result.payAddress = payAddress;
    // Consolidated network blob
    const network = {};
    if (capabilities.length)
        network.capabilities = capabilities.map(c => c.id || c.name);
    if (endpoints.length)
        network.endpoints = endpoints.map(e => e.url);
    if (protocols?.length)
        network.protocols = protocols;
    if (Object.keys(network).length > 0)
        result.network = network;
    // Consolidated profile blob
    const profileBlob = {};
    if (category)
        profileBlob.category = category;
    if (tags?.length)
        profileBlob.tags = tags;
    if (website)
        profileBlob.website = website;
    if (avatar)
        profileBlob.avatar = avatar;
    if (Object.keys(profileBlob).length > 0)
        result.profile = profileBlob;
    if (session)
        result.session = session;
    return result;
}
async function resolveServices(mode, services, hooks) {
    if (services && services.length)
        return services;
    if (mode !== 'interactive')
        return [];
    const prompt = hooks?.prompt || defaultPrompt;
    const result = [];
    let add = (await prompt('Add a service now? (y/N)', 'N')).toLowerCase();
    while (add === 'y' || add === 'yes') {
        const name = await prompt('  Service name');
        if (!name) {
            console.log('  Service name is required, skipping.');
            break;
        }
        const description = await prompt('  Service description (optional)', '');
        const category = await prompt('  Service category (optional)', 'general');
        const priceRaw = await prompt('  Price in satoshis (optional)', '0');
        // Default for testnet; set J41_NETWORK for mainnet
        const currency = await prompt('  Currency (optional)', 'VRSCTEST');
        const turnaround = await prompt('  Turnaround time (optional, e.g. "24h", "instant")', 'TBD');
        result.push({
            name,
            description: description || undefined,
            category: category || undefined,
            price: Number(priceRaw) || 0,
            currency: currency || undefined,
            turnaround: turnaround || undefined,
        });
        add = (await prompt('Add another service? (y/N)', 'N')).toLowerCase();
    }
    return result;
}
async function finalizeOnboarding(params) {
    const mode = params.mode || 'headless';
    const state = readState(params.statePath, mode, params.agent.identity, params.agent.address);
    const mark = (stage, note) => {
        state.stage = stage;
        if (note)
            state.notes = [...(state.notes || []), `${nowIso()} ${note}`];
        writeState(params.statePath, state);
    };
    // 1) VDXF publish
    if (['onboarded'].includes(state.stage)) {
        if (params.hooks?.publishVdxf) {
            await params.hooks.publishVdxf();
            mark('vdxf_published', 'VDXF definitions published');
        }
        else {
            mark('vdxf_published', 'VDXF publish hook not provided (deferred)');
        }
    }
    // 2) VDXF verify
    if (['vdxf_published'].includes(state.stage)) {
        if (params.hooks?.verifyVdxf) {
            await params.hooks.verifyVdxf();
            mark('vdxf_verified', 'VDXF definitions verified');
        }
        else {
            mark('vdxf_verified', 'VDXF verify hook not provided (deferred)');
        }
    }
    // 3) Index sync
    if (['vdxf_verified'].includes(state.stage)) {
        if (params.hooks?.waitForIndexed) {
            await params.hooks.waitForIndexed();
            mark('indexed', 'Index visibility confirmed');
        }
        else {
            mark('indexed', 'Index wait hook not provided (deferred)');
        }
    }
    // 4) Platform profile registration
    if (['indexed'].includes(state.stage)) {
        const profile = await resolveProfile(mode, params.profile, params.hooks);
        if (profile) {
            try {
                await params.agent.registerWithJ41(profile);
                mark('profile_registered', 'Agent profile registered');
            }
            catch (regErr) {
                // Re-authenticate and retry once (challenge may have expired during VDXF publish)
                if (regErr?.code === 'SIGNATURE_INVALID' || regErr?.statusCode === 401) {
                    try {
                        await params.agent.authenticate();
                        await params.agent.registerWithJ41(profile);
                        mark('profile_registered', 'Agent profile registered (retry after re-auth)');
                    }
                    catch (retryErr) {
                        // If still 409 (already registered), that's fine
                        if (retryErr?.statusCode === 409) {
                            mark('profile_registered', 'Agent already registered on platform');
                        }
                        else {
                            throw retryErr;
                        }
                    }
                }
                else {
                    throw regErr;
                }
            }
        }
        else {
            mark('profile_registered', 'Profile registration skipped');
        }
    }
    // 5) Service registration (optional by design)
    if (['profile_registered'].includes(state.stage)) {
        const services = await resolveServices(mode, params.services, params.hooks);
        for (const svc of services) {
            await params.agent.registerService(svc);
        }
        mark('services_registered', services.length ? `Registered ${services.length} service(s)` : 'No services registered');
    }
    if (state.stage === 'services_registered') {
        state.completedAt = nowIso();
        mark('ready', 'Finalize complete');
    }
    return state;
}
//# sourceMappingURL=finalize.js.map