import { BaseEngine } from "@memberjunction/core";
import { UUIDsEqual } from "@memberjunction/global";
/**
 * UserViewEngine is a singleton engine that provides centralized access to User Views.
 * It caches all views and provides efficient lookup methods for retrieving views by various criteria.
 *
 * This engine consolidates view loading into a single batched operation, improving performance
 * and enabling local caching for faster subsequent access.
 *
 * Usage:
 * ```typescript
 * // Initialize the engine on-demand before first use
 * await UserViewEngine.Instance.Config(false);
 *
 * // Then access views
 * const myViews = engine.GetViewsForCurrentUser();
 * const entityViews = engine.GetViewsForEntity('entityId');
 * const view = engine.GetViewById('viewId');
 * ```
 *
 * Note: This engine is NOT auto-started at application startup. You must call Config() before use.
 * Views are cached globally. Use the filtering methods to get views for specific users or entities.
 */
export class UserViewEngine extends BaseEngine {
    constructor() {
        super(...arguments);
        // Private storage for view data
        this._views = [];
        // Track the user ID for filtering purposes
        this._contextUserId = null;
    }
    /**
     * Returns the global instance of the class. This is a singleton class, so there is only one instance of it in the application.
     * Do not directly create new instances of it, always use this method to get the instance.
     */
    static get Instance() {
        return super.getInstance();
    }
    /**
     * Configures the engine by loading all User Views from the database.
     * Views are cached locally for performance.
     *
     * @param forceRefresh - If true, forces a refresh from the server even if data is cached
     * @param contextUser - The user context (required for server-side, auto-detected on client)
     * @param provider - Optional custom metadata provider
     */
    async Config(forceRefresh, contextUser, provider) {
        const md = provider ?? this.ProviderToUse;
        const userId = contextUser?.ID || md.CurrentUser?.ID;
        console.debug(`[UserViewEngine] Config() called: loaded=${this.Loaded}, forceRefresh=${forceRefresh}, viewCount=${this._views?.length ?? 0}`);
        // Store the context user ID for filtering
        this._contextUserId = userId || null;
        const configs = [
            {
                Type: 'entity',
                EntityName: 'MJ: User Views',
                PropertyName: '_views',
                CacheLocal: true
            }
        ];
        await super.Load(configs, provider, forceRefresh, contextUser);
        console.debug(`[UserViewEngine] Config() complete: viewCount=${this._views?.length ?? 0}`);
    }
    // ========================================================================
    // PUBLIC ACCESSORS - ALL VIEWS
    // ========================================================================
    /**
     * Get all views in the cache (unfiltered)
     */
    get AllViews() {
        return this.GetConfigData('_views');
    }
    /**
     * Get a view by its ID
     * @param viewId - The view ID to find
     * @returns The view entity or undefined if not found
     */
    GetViewById(viewId) {
        return this.AllViews.find(v => UUIDsEqual(v.ID, viewId));
    }
    /**
     * Get a view by its name
     * @param viewName - The view name to find
     * @returns The view entity or undefined if not found
     */
    GetViewByName(viewName) {
        return this.AllViews.find(v => v.Name.toLowerCase() === viewName.toLowerCase());
    }
    // ========================================================================
    // FILTERED ACCESSORS - BY ENTITY
    // ========================================================================
    /**
     * Get all views for a specific entity
     * @param entityId - The entity ID to filter by
     * @returns Array of views for the entity
     */
    GetViewsForEntity(entityId) {
        return this.AllViews
            .filter(v => UUIDsEqual(v.EntityID, entityId))
            .sort((a, b) => a.Name.localeCompare(b.Name));
    }
    /**
     * Get all views for a specific entity by entity name
     * @param entityName - The entity name to filter by
     * @returns Array of views for the entity
     */
    GetViewsForEntityByName(entityName) {
        const md = this.ProviderToUse;
        const entity = md.EntityByName(entityName);
        if (!entity)
            return [];
        return this.GetViewsForEntity(entity.ID);
    }
    // ========================================================================
    // FILTERED ACCESSORS - BY USER
    // ========================================================================
    /**
     * Get all views owned by the current user (based on context user)
     * @returns Array of views owned by the current user
     */
    GetViewsForCurrentUser() {
        if (!this._contextUserId)
            return [];
        return this.GetViewsForUser(this._contextUserId);
    }
    /**
     * Get all views owned by a specific user
     * @param userId - The user ID to filter by
     * @returns Array of views owned by the user
     */
    GetViewsForUser(userId) {
        return this.AllViews
            .filter(v => UUIDsEqual(v.UserID, userId))
            .sort((a, b) => a.Name.localeCompare(b.Name));
    }
    /**
     * Get all shared views (views marked as shared that the user doesn't own)
     * @returns Array of shared views
     */
    GetSharedViews() {
        return this.AllViews
            .filter(v => v.IsShared && !UUIDsEqual(v.UserID, this._contextUserId))
            .sort((a, b) => a.Name.localeCompare(b.Name));
    }
    /**
     * Get all views accessible to the current user for a specific entity
     * (includes owned views and shared views)
     * @param entityId - The entity ID to filter by
     * @returns Array of accessible views for the entity
     */
    GetAccessibleViewsForEntity(entityId) {
        if (!this._contextUserId) {
            // No user context - return all views for entity
            return this.GetViewsForEntity(entityId);
        }
        return this.AllViews
            .filter(v => UUIDsEqual(v.EntityID, entityId) &&
            (UUIDsEqual(v.UserID, this._contextUserId) || v.IsShared))
            .filter(v => v.UserCanView) // Respect permission checks
            .sort((a, b) => {
            // Sort: owned first, then by name
            const aOwned = UUIDsEqual(a.UserID, this._contextUserId);
            const bOwned = UUIDsEqual(b.UserID, this._contextUserId);
            if (aOwned && !bOwned)
                return -1;
            if (!aOwned && bOwned)
                return 1;
            return a.Name.localeCompare(b.Name);
        });
    }
    // ========================================================================
    // FILTERED ACCESSORS - COMBINED
    // ========================================================================
    /**
     * Get views for a specific entity owned by the current user
     * @param entityId - The entity ID to filter by
     * @returns Array of views for the entity owned by the current user
     */
    GetMyViewsForEntity(entityId) {
        if (!this._contextUserId)
            return [];
        return this.AllViews
            .filter(v => UUIDsEqual(v.EntityID, entityId) && UUIDsEqual(v.UserID, this._contextUserId))
            .sort((a, b) => a.Name.localeCompare(b.Name));
    }
    /**
     * Get shared views for a specific entity (not owned by current user)
     * @param entityId - The entity ID to filter by
     * @returns Array of shared views for the entity
     */
    GetSharedViewsForEntity(entityId) {
        return this.AllViews
            .filter(v => UUIDsEqual(v.EntityID, entityId) &&
            v.IsShared &&
            !UUIDsEqual(v.UserID, this._contextUserId))
            .filter(v => v.UserCanView)
            .sort((a, b) => a.Name.localeCompare(b.Name));
    }
    /**
     * Get the default view for an entity for the current user
     * @param entityId - The entity ID to find default view for
     * @returns The default view or undefined if none exists
     */
    GetDefaultViewForEntity(entityId) {
        const myViews = this.GetMyViewsForEntity(entityId);
        return myViews.find(v => v.IsDefault);
    }
    // ========================================================================
    // UTILITY METHODS
    // ========================================================================
    /**
     * Check if a view exists by ID
     * @param viewId - The view ID to check
     * @returns True if the view exists
     */
    ViewExists(viewId) {
        return this.GetViewById(viewId) !== undefined;
    }
    /**
     * Check if a view with the given name exists for an entity
     * @param entityId - The entity ID
     * @param viewName - The view name to check
     * @returns True if a view with that name exists for the entity
     */
    ViewNameExistsForEntity(entityId, viewName) {
        return this.AllViews.some(v => UUIDsEqual(v.EntityID, entityId) &&
            v.Name.toLowerCase() === viewName.toLowerCase());
    }
    /**
     * Force refresh the view cache
     * Useful after creating, updating, or deleting a view
     */
    async RefreshCache(contextUser, provider) {
        await this.Config(true, contextUser, provider);
    }
    /**
     * Get the current context user ID
     */
    get ContextUserId() {
        return this._contextUserId;
    }
}
//# sourceMappingURL=UserViewEngine.js.map