/**
 * SlackUserRegistry — conversational registration + role assignment (Phase 1).
 *
 * Slice 0 made the gate enforce roles; this makes registration first-class:
 *   - admin registers a Slack user with a role ("register Sarah as a developer")
 *   - an unregistered user's request becomes a PENDING registration → admin approval
 *   - approve/deny resolves it (approve creates the UserProfile with the role)
 *
 * All conversational — never a CLI, never asking the user to edit files. Pending
 * registrations are durable so an approval survives restarts. Decoupled from core:
 * depends only on a minimal user-store interface (UserManager satisfies it).
 *
 * Design: docs/specs/SLACK-ORG-INTEGRATION-SPEC.md §6.3.
 */
import fs from 'node:fs';
import path from 'node:path';
import { ORG_ROLES } from './types.js';
function isValidRole(role) {
    return ORG_ROLES.includes(role);
}
/** Build a fresh UserProfile for a newly-registered Slack user. */
function buildProfile(slackUserId, displayName, role, now) {
    return {
        id: `slack-${slackUserId}`,
        name: displayName || slackUserId,
        channels: [{ type: 'slack', identifier: slackUserId }],
        permissions: [role],
        preferences: {},
        slackUserId,
        orgRole: role,
        createdAt: now,
    };
}
export class SlackUserRegistry {
    users;
    now;
    pendingFile;
    constructor(users, stateDir, now = () => new Date().toISOString()) {
        this.users = users;
        this.now = now;
        /* state-registry: slack-pending-registrations */
        this.pendingFile = path.join(stateDir, 'slack-pending-registrations.json');
    }
    /** Admin-initiated registration: create/assign the role immediately. Throws on invalid role. */
    register(slackUserId, displayName, role) {
        if (!slackUserId)
            throw new Error('slackUserId is required');
        if (!isValidRole(role))
            throw new Error(`invalid role "${role}" (expected one of: ${ORG_ROLES.join(', ')})`);
        const profile = buildProfile(slackUserId, displayName, role, this.now());
        this.users.upsertUser(profile);
        this.removePending(slackUserId); // a direct registration clears any pending request
        return profile;
    }
    /** True iff this Slack user is already registered. */
    isRegistered(slackUserId) {
        return !!this.users.resolveFromSlackUserId(slackUserId);
    }
    /**
     * Self-registration request from an unregistered user → durable PENDING entry for
     * admin approval. No-op (returns existing) if already pending; null if already registered.
     */
    requestRegistration(slackUserId, displayName, channel) {
        if (this.isRegistered(slackUserId))
            return null;
        const pending = this.readPending();
        const existing = pending.find((p) => p.slackUserId === slackUserId);
        if (existing)
            return existing;
        const entry = { slackUserId, displayName, requestedAt: this.now(), channel };
        pending.push(entry);
        this.writePending(pending);
        return entry;
    }
    listPending() {
        return this.readPending();
    }
    /** Approve a pending registration with a role → creates the UserProfile, clears the pending entry. */
    approve(slackUserId, role) {
        if (!isValidRole(role))
            throw new Error(`invalid role "${role}" (expected one of: ${ORG_ROLES.join(', ')})`);
        const pending = this.readPending();
        const entry = pending.find((p) => p.slackUserId === slackUserId);
        const displayName = entry?.displayName || slackUserId;
        const profile = this.register(slackUserId, displayName, role);
        return profile;
    }
    /** Deny (drop) a pending registration. Returns true if one was removed. */
    deny(slackUserId) {
        return this.removePending(slackUserId);
    }
    // ── pending-store persistence (durable JSON) ──
    readPending() {
        try {
            return JSON.parse(fs.readFileSync(this.pendingFile, 'utf8'));
        }
        catch {
            return [];
        }
    }
    writePending(entries) {
        fs.mkdirSync(path.dirname(this.pendingFile), { recursive: true });
        fs.writeFileSync(this.pendingFile, JSON.stringify(entries, null, 2) + '\n');
    }
    removePending(slackUserId) {
        const pending = this.readPending();
        const next = pending.filter((p) => p.slackUserId !== slackUserId);
        if (next.length === pending.length)
            return false;
        this.writePending(next);
        return true;
    }
}
//# sourceMappingURL=SlackUserRegistry.js.map