import { configInfo } from '../config.js';
import { UserCache } from '@memberjunction/sqlserver-dataprovider';
import { Metadata, UserInfo } from '@memberjunction/core';
import { NewUserBase } from './newUsers.js';
import { MJGlobal } from '@memberjunction/global';
import { AuthProviderFactory } from './AuthProviderFactory.js';
import { initializeAuthProviders } from './initializeProviders.js';
export { TokenExpiredError } from './tokenExpiredError.js';
export { AuthProviderFactory } from './AuthProviderFactory.js';
class MissingAuthError extends Error {
    constructor() {
        super('No authentication providers configured. Please configure at least one auth provider in mj.config.cjs');
        this.name = 'MissingAuthError';
    }
}
const refreshUserCache = async (dataSource) => {
    const startTime = Date.now();
    await UserCache.Instance.Refresh(dataSource);
    const endTime = Date.now();
    const elapsed = endTime - startTime;
    const delay = configInfo.userHandling.updateCacheWhenNotFoundDelay
        ? configInfo.userHandling.updateCacheWhenNotFoundDelay < 30000
            ? configInfo.userHandling.updateCacheWhenNotFoundDelay
            : 30000
        : 0;
    if (elapsed < delay)
        await new Promise((resolve) => setTimeout(resolve, delay - elapsed));
    const finalTime = Date.now();
    const finalElapsed = finalTime - startTime;
    console.log(`   UserCache updated in ${elapsed}ms, total elapsed time of ${finalElapsed}ms including delay of ${delay}ms (if needed). Attempting to find the user again via recursive call`);
};
export const getValidationOptions = (issuer) => {
    const factory = AuthProviderFactory.getInstance();
    const provider = factory.getByIssuer(issuer);
    if (!provider) {
        return undefined;
    }
    return {
        audience: provider.audience,
        jwksUri: provider.jwksUri
    };
};
export const validationOptions = new Proxy({}, {
    get: (target, prop) => {
        return getValidationOptions(prop);
    },
    has: (target, prop) => {
        return getValidationOptions(prop) !== undefined;
    },
    ownKeys: () => {
        const factory = AuthProviderFactory.getInstance();
        return factory.getAllProviders().map(p => p.issuer);
    }
});
export class UserPayload {
    aio;
    aud;
    exp;
    iat;
    iss;
    name;
    nbf;
    nonce;
    oid;
    preferred_username;
    rh;
    sub;
    tid;
    uti;
    ver;
    email;
    given_name;
    family_name;
}
export const getSigningKeys = (issuer) => (header, cb) => {
    const factory = AuthProviderFactory.getInstance();
    if (!factory.hasProviders()) {
        initializeAuthProviders();
    }
    const provider = factory.getByIssuer(issuer);
    if (!provider) {
        if (!factory.hasProviders()) {
            throw new MissingAuthError();
        }
        throw new Error(`No authentication provider found for issuer: ${issuer}`);
    }
    provider.getSigningKey(header, cb);
};
export const extractUserInfoFromPayload = (payload) => {
    const factory = AuthProviderFactory.getInstance();
    const issuer = payload.iss;
    if (!issuer) {
        const preferredUsername = payload.preferred_username;
        return {
            email: payload.email || preferredUsername,
            firstName: payload.given_name,
            lastName: payload.family_name,
            fullName: payload.name,
            preferredUsername
        };
    }
    const provider = factory.getByIssuer(issuer);
    if (!provider) {
        const fullName = payload.name;
        const preferredUsername = payload.preferred_username;
        return {
            email: payload.email || preferredUsername,
            firstName: payload.given_name || fullName?.split(' ')[0],
            lastName: payload.family_name || fullName?.split(' ')[1] || fullName?.split(' ')[0],
            fullName,
            preferredUsername
        };
    }
    return provider.extractUserInfo(payload);
};
export const getSystemUser = async (dataSource, attemptCacheUpdateIfNeeded = true) => {
    const systemUser = UserCache.Instance.GetSystemUser();
    if (!systemUser) {
        if (dataSource && attemptCacheUpdateIfNeeded) {
            console.warn(`System user not found in cache. Updating cache in attempt to find the user...`);
            await refreshUserCache(dataSource);
            return getSystemUser(dataSource, false);
        }
        throw new Error(`System user ID '${UserCache.Instance.SYSTEM_USER_ID}' not found in database`);
    }
    return systemUser;
};
export const verifyUserRecord = async (email, firstName, lastName, requestDomain, dataSource, attemptCacheUpdateIfNeeded = true) => {
    if (!email)
        return undefined;
    let user = UserCache.Instance.Users.find((u) => {
        if (!u.Email || u.Email.trim() === '') {
            console.error(`SYSTEM METADATA ISSUE: User ${u.ID} has no email address`);
            return false;
        }
        else
            return u.Email.toLowerCase().trim() === email.toLowerCase().trim();
    });
    if (!user) {
        if (configInfo.userHandling.autoCreateNewUsers &&
            firstName &&
            lastName &&
            (requestDomain || configInfo.userHandling.newUserLimitedToAuthorizedDomains === false)) {
            let passesDomainCheck = configInfo.userHandling.newUserLimitedToAuthorizedDomains ===
                false;
            if (!passesDomainCheck && requestDomain) {
                passesDomainCheck = configInfo.userHandling.newUserAuthorizedDomains.some((pattern) => {
                    const regex = new RegExp('^' + pattern.toLowerCase().trim().replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
                    return regex.test(requestDomain?.toLowerCase().trim());
                });
            }
            if (passesDomainCheck) {
                console.warn(`User ${email} not found in cache. Attempting to create a new user...`);
                const newUserCreator = MJGlobal.Instance.ClassFactory.CreateInstance(NewUserBase);
                const newUser = await newUserCreator.createNewUser(firstName, lastName, email);
                if (newUser) {
                    const md = new Metadata();
                    const initData = newUser.GetAll();
                    initData.UserRoles = configInfo.userHandling.newUserRoles.map((role) => {
                        const roleInfo = md.Roles.find((r) => r.Name === role);
                        const roleID = roleInfo ? roleInfo.ID : '';
                        return { UserID: initData.ID, RoleName: role, RoleID: roleID };
                    });
                    user = new UserInfo(Metadata.Provider, initData);
                    UserCache.Instance.Users.push(user);
                    console.warn(`   >>> New user ${email} created successfully!`);
                }
            }
            else {
                console.warn(`User ${email} not found in cache. Request domain '${requestDomain}' does not match any of the domains in the newUserAuthorizedDomains setting. To ignore domain, make sure you set the newUserLimitedToAuthorizedDomains setting to false. In this case we are NOT creating a new user.`);
            }
        }
        if (!user && configInfo.userHandling.updateCacheWhenNotFound && dataSource && attemptCacheUpdateIfNeeded) {
            console.warn(`User ${email} not found in cache. Updating cache in attempt to find the user...`);
            await refreshUserCache(dataSource);
            return verifyUserRecord(email, firstName, lastName, requestDomain, dataSource, false);
        }
    }
    return user;
};
initializeAuthProviders();
//# sourceMappingURL=index.js.map