"use strict";
/**
 * ProfileManager — runtime profile manager.
 *
 * Story 5.2 — OpenLife v1.3 Agent OS Integration.
 *
 * Wraps the existing InstallStateStore + AgentManager surface with a
 * per-profile namespace concept. A Profile carries:
 *   - id, name, description
 *   - memoryNamespace (passed through to OmniMemory)
 *   - catalogScope[]  (limits which .catalog/ subtrees are visible)
 *   - toolsetAllowed[] (controls ToolsetRegistry — Story 5.3)
 *   - modelChain (optional override of ModelManager primary+fallbacks)
 *   - gatewaySettings (Telegram allowlist, /health metadata)
 *
 * Storage: `.openlife/profiles/<id>.json` + `.openlife/active-profile.json`
 *
 * Profiles default to global scope (catalogScope=[], toolsetAllowed=[*])
 * for backwards compatibility with existing AgentProfile entries.
 *
 * Lifecycle is fully append-only: create/update produce a new file
 * (atomic write); use(<id>) just updates the active-profile pointer.
 * No deletion in v1.3 — call use(<other>) and ignore the old file.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProfileManager = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const AtomicWriter_1 = require("../orchestrator/util/AtomicWriter");
class ProfileManager {
    profilesDir;
    activeFile;
    constructor(opts = {}) {
        const stateDir = opts.stateDir
            || process.env.OPENLIFE_STATE_DIR
            || path.join(process.cwd(), '.openlife');
        this.profilesDir = path.join(stateDir, 'profiles');
        this.activeFile = path.join(stateDir, 'active-profile.json');
        fs.mkdirSync(this.profilesDir, { recursive: true });
    }
    profilePath(id) {
        const safe = id.replace(/[^a-zA-Z0-9._-]/g, '_');
        return path.join(this.profilesDir, `${safe}.json`);
    }
    list() {
        if (!fs.existsSync(this.profilesDir))
            return [];
        const out = [];
        for (const f of fs.readdirSync(this.profilesDir)) {
            if (!f.endsWith('.json'))
                continue;
            try {
                out.push(JSON.parse(fs.readFileSync(path.join(this.profilesDir, f), 'utf-8')));
            }
            catch { /* skip corrupt */ }
        }
        return out;
    }
    show(id) {
        const p = this.profilePath(id);
        if (!fs.existsSync(p))
            return null;
        try {
            return JSON.parse(fs.readFileSync(p, 'utf-8'));
        }
        catch {
            return null;
        }
    }
    create(opts) {
        if (!/^[a-z0-9][a-z0-9._-]+$/i.test(opts.id)) {
            throw Object.assign(new Error('invalid_profile_id'), { code: 'invalid_profile_id' });
        }
        const file = this.profilePath(opts.id);
        if (fs.existsSync(file)) {
            throw Object.assign(new Error('profile_already_exists'), { code: 'profile_already_exists' });
        }
        const now = new Date().toISOString();
        const profile = {
            id: opts.id,
            name: opts.name,
            description: opts.description,
            memoryNamespace: opts.memoryNamespace || `profile-${opts.id}`,
            catalogScope: opts.catalogScope || [],
            toolsetAllowed: opts.toolsetAllowed || ['*'],
            modelChain: opts.modelChain,
            gatewaySettings: opts.gatewaySettings,
            createdAt: now,
            updatedAt: now,
        };
        (0, AtomicWriter_1.writeJsonAtomic)(file, profile);
        return profile;
    }
    /** Set the active profile pointer. The profile must already exist. */
    use(id) {
        if (!this.show(id))
            return { ok: false, error: 'not_found' };
        (0, AtomicWriter_1.writeJsonAtomic)(this.activeFile, { activeProfileId: id, at: new Date().toISOString() });
        return { ok: true };
    }
    /** Return the active profile id, or null if none set. */
    active() {
        if (!fs.existsSync(this.activeFile))
            return { profileId: null, profile: null };
        try {
            const ptr = JSON.parse(fs.readFileSync(this.activeFile, 'utf-8'));
            const id = ptr.activeProfileId || null;
            return { profileId: id, profile: id ? this.show(id) : null };
        }
        catch {
            return { profileId: null, profile: null };
        }
    }
    /** Export a profile as plain JSON (for import on another machine). */
    export(id) {
        return this.show(id);
    }
    /** Import a profile from a JSON object. Idempotent — overwrites on id collision. */
    import(profile) {
        if (!profile?.id || !/^[a-z0-9][a-z0-9._-]+$/i.test(profile.id)) {
            return { ok: false, error: 'invalid_profile_id' };
        }
        (0, AtomicWriter_1.writeJsonAtomic)(this.profilePath(profile.id), {
            ...profile,
            updatedAt: new Date().toISOString(),
        });
        return { ok: true };
    }
}
exports.ProfileManager = ProfileManager;
