import { __decorate, __metadata } from "tslib";
import { GraphQLError } from 'graphql';
import { Injectable, Scope } from 'graphql-modules';
import { narrowReadScope, readScopeFromMemberships } from '../../../shared/helpers/auth-scope.js';
import { AdminContextProvider } from '../../admin-context/providers/admin-context.provider.js';
import { AuthContextProvider } from './auth-context.provider.js';
/**
 * Single, reusable entry point for request-level business scope decisions.
 *
 * Resolvers and providers should use this rather than re-implementing scope
 * narrowing or reaching into the admin context per module:
 * - reads default to all accessible businesses, narrowed by request args;
 * - writes require an explicit, membership-validated single target;
 * - per-business preferences are resolved within the authorized read scope.
 */
let ScopeProvider = class ScopeProvider {
    authContextProvider;
    adminContextProvider;
    constructor(authContextProvider, adminContextProvider) {
        this.authContextProvider = authContextProvider;
        this.adminContextProvider = adminContextProvider;
    }
    async requireAuthContext() {
        const authContext = await this.authContextProvider.getAuthContext();
        if (!authContext) {
            throw new GraphQLError('Authentication required', {
                extensions: { code: 'UNAUTHENTICATED' },
            });
        }
        return authContext;
    }
    /**
     * The effective read scope: the request's authorized scope
     * (header ∩ memberships, resolved by the auth context) narrowed by the
     * optionally-provided args business ids. Defaults to all accessible
     * businesses. Throws when args request a business outside the authorized
     * scope (callers must reject, never broaden).
     */
    async getReadScope(argsBusinessIds) {
        const authContext = await this.requireAuthContext();
        const baseScope = authContext.activeReadScope ?? readScopeFromMemberships(authContext.memberships ?? []);
        if (!argsBusinessIds || argsBusinessIds.length === 0) {
            return baseScope.businessIds;
        }
        const allowed = baseScope.businessIds.map(businessId => ({ businessId, roleId: '' }));
        const narrowed = narrowReadScope(allowed, argsBusinessIds);
        if (!narrowed) {
            throw new GraphQLError('Requested business scope is outside the authorized read scope', {
                extensions: { code: 'FORBIDDEN' },
            });
        }
        return narrowed.businessIds;
    }
    /**
     * Validate and return the single write-target business. Writes always take an
     * explicit business id (never inferred from the read scope); the target must
     * be one of the user's memberships.
     */
    async resolveWriteTarget(requestedBusinessId) {
        const authContext = await this.requireAuthContext();
        if (!requestedBusinessId) {
            throw new GraphQLError('An explicit target businessId is required for writes', {
                extensions: { code: 'BAD_USER_INPUT' },
            });
        }
        const isMember = (authContext.memberships ?? []).some(membership => membership.businessId === requestedBusinessId);
        if (!isMember) {
            throw new GraphQLError('Not authorized to write to the requested business', {
                extensions: { code: 'FORBIDDEN' },
            });
        }
        return requestedBusinessId;
    }
    /**
     * Resolve a single business preference (e.g. `defaultLocalCurrency`) for a
     * specific business within the authorized read scope. Replaces per-field
     * reads of the request's single admin context, so multi-business reads can
     * resolve preferences from each row's owning business.
     */
    async getBusinessPreference(businessId, key) {
        const scope = await this.getReadScope();
        if (!scope.includes(businessId)) {
            throw new GraphQLError('Business is outside the authorized read scope', {
                extensions: { code: 'FORBIDDEN' },
            });
        }
        const adminContext = await this.adminContextProvider.adminContextByOwnerIdLoader.load(businessId);
        return adminContext ? adminContext[key] : null;
    }
};
ScopeProvider = __decorate([
    Injectable({
        scope: Scope.Operation,
        global: true,
    }),
    __metadata("design:paramtypes", [AuthContextProvider,
        AdminContextProvider])
], ScopeProvider);
export { ScopeProvider };
//# sourceMappingURL=scope.provider.js.map