import { __decorate, __metadata, __param } from "tslib";
import { ManagementClient } from 'auth0';
import { Inject, Injectable, Scope } from 'graphql-modules';
import { ENVIRONMENT } from '../../../shared/tokens.js';
let Auth0ManagementProvider = class Auth0ManagementProvider {
    env;
    client;
    // Short-lived cache of Auth0 profile lookups. The provider is a Singleton, so
    // this is shared across requests to avoid hammering the Auth0 Management API
    // (and its rate limits) when listing users. The TTL bounds staleness so
    // profile changes in Auth0 still propagate within a few minutes.
    profileCache = new Map();
    PROFILE_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
    constructor(env) {
        this.env = env;
        this.initializeClient();
    }
    initializeClient() {
        if (this.env.auth0) {
            this.client = new ManagementClient({
                domain: this.env.auth0.domain,
                clientId: this.env.auth0.clientId,
                clientSecret: this.env.auth0.clientSecret,
                audience: this.env.auth0.managementAudience,
            });
        }
        else {
            console.warn('Auth0 not configured, Auth0ManagementProvider disabled');
        }
    }
    getClient() {
        if (!this.client) {
            throw new Error('Auth0 client not initialized');
        }
        return this.client;
    }
    async getUserByEmail(email) {
        const client = this.getClient();
        try {
            const users = await client.users.listUsersByEmail({ email });
            if (users?.length > 0) {
                return users[0].user_id ?? null;
            }
            return null;
        }
        catch (error) {
            console.error(`Failed to get Auth0 user by email ${email}:`, error);
            throw new Error(`Failed to get Auth0 user by email: ${error.message}`, {
                cause: error,
            });
        }
    }
    async getUserEmailById(auth0UserId) {
        const client = this.getClient();
        try {
            const user = await client.users.get(auth0UserId);
            return {
                email: typeof user?.email === 'string' ? user.email : null,
                emailVerified: user?.email_verified === true,
            };
        }
        catch (error) {
            console.error(`Failed to get Auth0 user by id ${auth0UserId}:`, error);
            return null;
        }
    }
    /**
     * Fetch the display identity (email + name) for an Auth0 user.
     * Returns null when the lookup fails so callers can degrade gracefully
     * instead of failing the whole request.
     */
    async getUserProfileById(auth0UserId) {
        const cached = this.profileCache.get(auth0UserId);
        if (cached && cached.expiresAt > Date.now()) {
            return cached.profile;
        }
        const client = this.getClient();
        try {
            const user = await client.users.get(auth0UserId);
            const profile = {
                email: typeof user?.email === 'string' ? user.email : null,
                name: typeof user?.name === 'string' ? user.name : null,
            };
            // Only successful lookups are cached, so transient failures are retried.
            this.profileCache.set(auth0UserId, {
                profile,
                expiresAt: Date.now() + this.PROFILE_CACHE_TTL_MS,
            });
            return profile;
        }
        catch (error) {
            console.error(`Failed to get Auth0 user profile by id ${auth0UserId}:`, error);
            return null;
        }
    }
    async createBlockedUser(email, password) {
        const client = this.getClient();
        const existingUserId = await this.getUserByEmail(email);
        if (existingUserId) {
            return existingUserId;
        }
        const temporaryPassword = password || this.generateTemporaryPassword();
        try {
            const user = await client.users.create({
                connection: 'Username-Password-Authentication',
                email,
                password: temporaryPassword,
                email_verified: false,
                blocked: true,
                app_metadata: {
                    registered_by: 'accounter',
                },
            });
            if (!user.user_id) {
                throw new Error('Auth0 did not return a user ID');
            }
            return user.user_id;
        }
        catch (error) {
            console.error('Failed to create Auth0 user:', error);
            throw new Error(`Failed to create Auth0 user: ${error.message}`, { cause: error });
        }
    }
    async unblockUser(auth0UserId) {
        const client = this.getClient();
        try {
            await client.users.update(auth0UserId, { blocked: false });
        }
        catch (error) {
            console.error(`Failed to unblock Auth0 user ${auth0UserId}:`, error);
            throw new Error(`Failed to unblock Auth0 user: ${error.message}`, {
                cause: error,
            });
        }
    }
    async blockUser(auth0UserId) {
        const client = this.getClient();
        try {
            await client.users.update(auth0UserId, { blocked: true });
        }
        catch (error) {
            console.error(`Failed to block Auth0 user ${auth0UserId}:`, error);
            throw new Error(`Failed to block Auth0 user: ${error.message}`, { cause: error });
        }
    }
    async deleteUser(auth0UserId) {
        const client = this.getClient();
        try {
            // Cleanup for expired invitations
            await client.users.delete(auth0UserId);
        }
        catch (error) {
            console.error(`Failed to delete Auth0 user ${auth0UserId}:`, error);
            throw new Error(`Failed to delete Auth0 user: ${error.message}`, { cause: error });
        }
    }
    async sendPasswordResetEmail(auth0UserId) {
        const client = this.getClient();
        try {
            const { ticket } = await client.tickets.changePassword({
                user_id: auth0UserId,
                mark_email_as_verified: true, // Mark verified only after password change
                result_url: this.env.general.frontendUrl
                    ? `${this.env.general.frontendUrl}/login`
                    : undefined,
            });
            return ticket;
        }
        catch (error) {
            console.error('Failed to trigger password change', error);
            throw new Error(`Failed to trigger password change: ${error.message}`, {
                cause: error,
            });
        }
    }
    generateTemporaryPassword() {
        // Generate a random password that meets strict Auth0 requirements:
        // 8+ chars, lower, upper, number, special char
        const lower = 'abcdefghijklmnopqrstuvwxyz';
        const upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        const numbers = '0123456789';
        const special = '!@#$%^&*()_+~`|}{[]\\:;?><,./-=';
        const all = lower + upper + numbers + special;
        const pick = (str) => {
            const randomValues = new Uint32Array(1);
            globalThis.crypto.getRandomValues(randomValues);
            return str[randomValues[0] % str.length];
        };
        const password = pick(lower) +
            pick(upper) +
            pick(numbers) +
            pick(special) +
            Array.from({ length: 12 }, () => pick(all)).join('');
        return password;
    }
};
Auth0ManagementProvider = __decorate([
    Injectable({
        scope: Scope.Singleton,
        global: true,
    }),
    __param(0, Inject(ENVIRONMENT)),
    __metadata("design:paramtypes", [Object])
], Auth0ManagementProvider);
export { Auth0ManagementProvider };
//# sourceMappingURL=auth0-management.provider.js.map