"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.withAuthGuard = exports.createAuthGuard = void 0;
exports.registerAuthTools = registerAuthTools;
const v3_1 = require("zod/v3");
const ConfigManager_1 = require("../../services/ConfigManager");
const telemetry_1 = require("../../config/telemetry");
const logger_1 = require("../../utils/logger");
const log = (0, logger_1.createLogger)('mcp-auth');
function jsonResponse(payload) {
    return {
        content: [{ type: 'text', text: JSON.stringify(payload) }],
    };
}
function errorResponse(message, code) {
    return {
        content: [{ type: 'text', text: JSON.stringify({ error: message, code }) }],
        isError: true,
    };
}
const registerTool = (mcp, name, description, shape, handler) => {
    const tool = mcp.tool.bind(mcp);
    tool(name, description, shape, handler);
};
/**
 * Build a reusable guard for tools that require authentication. Returns null
 * when the caller is genuinely logged in (server-validated, refreshing a stale
 * token if needed), otherwise a ready-to-return auth-required tool response.
 */
const createAuthGuard = (authService) => {
    return async () => {
        const ok = await authService.ensureAuthenticated();
        if (ok) {
            return null;
        }
        return errorResponse('Authentication required. Ask the user to run the `login` tool (it opens a browser to sign in with Google), then retry.', 'AUTH_REQUIRED');
    };
};
exports.createAuthGuard = createAuthGuard;
/**
 * Wrap an McpServer so EVERY tool registered through it is gated behind `guard`:
 * the guard runs before the tool's handler and, when it returns a response
 * (unauthenticated), that response is returned instead of running the tool.
 * Register login/logout/auth_status on the RAW server (not this wrapper) so
 * they stay reachable while logged out.
 *
 * Implemented as a Proxy over `.tool` so call sites don't change — the handler
 * is the last argument regardless of which SDK overload is used.
 */
const withAuthGuard = (mcp, guard) => {
    return new Proxy(mcp, {
        get(target, prop, receiver) {
            if (prop !== 'tool') {
                return Reflect.get(target, prop, receiver);
            }
            const originalTool = target.tool.bind(target);
            return (...args) => {
                const lastIdx = args.length - 1;
                const handler = args[lastIdx];
                if (typeof handler === 'function') {
                    const original = handler;
                    args[lastIdx] = async (...callArgs) => {
                        const denied = await guard();
                        if (denied)
                            return denied;
                        return original(...callArgs);
                    };
                }
                return originalTool(...args);
            };
        },
    });
};
exports.withAuthGuard = withAuthGuard;
function registerAuthTools(mcp, deps) {
    const { authService, telemetry, mcpEditor } = deps;
    registerTool(mcp, 'login', 'Authenticate this machine with Rivet via Google. Opens the user\'s browser to sign in, then blocks (~up to 2 minutes) until they finish. If already signed in with a valid session, returns immediately. On timeout, returns success:false with a retryable message — call login again to keep waiting.', {}, async () => {
        try {
            // Real validation — only short-circuit on a genuinely live session,
            // never on a merely-present (possibly stale) token.
            const existing = await authService.getValidatedProfile();
            if (existing) {
                return jsonResponse({
                    success: true,
                    alreadyAuthenticated: true,
                    email: (0, ConfigManager_1.getConfigManager)().getEmail(),
                    userId: (0, ConfigManager_1.getConfigManager)().getUserId(),
                    profile: existing.profile,
                });
            }
            telemetry?.track('mcp_login_started', { source: 'mcp' });
            const result = await authService.loginWithGoogle({
                source: 'mcp',
                editor: mcpEditor,
                version: telemetry_1.RIVET_VERSION,
            });
            if (!result.success) {
                telemetry?.track('mcp_login_failed', {
                    source: 'mcp',
                    error_type: result.error ?? 'unknown',
                });
                return jsonResponse({
                    success: false,
                    error: result.error,
                    message: result.error === 'Timeout'
                        ? 'Login timed out. Call the login tool again to keep waiting for the browser sign-in.'
                        : result.message,
                });
            }
            // setAuth already ran inside loginWithGoogle; sync PostHog identity.
            telemetry?.identifyUser();
            telemetry?.track('mcp_login_completed', { source: 'mcp' });
            return jsonResponse({
                success: true,
                email: result.user?.email,
                userId: result.user?.id ?? (0, ConfigManager_1.getConfigManager)().getUserId(),
                profile: result.profile,
            });
        }
        catch (err) {
            log.warn('login tool error:', err);
            return errorResponse(err instanceof Error ? err.message : 'Login failed', 'LOGIN_FAILED');
        }
    });
    registerTool(mcp, 'logout', 'Sign this machine out of Rivet and clear the stored credentials.', {}, async () => {
        try {
            const email = (0, ConfigManager_1.getConfigManager)().getEmail();
            await authService.logout();
            telemetry?.track('mcp_logout', { source: 'mcp' });
            return jsonResponse({ loggedOut: true, email });
        }
        catch (err) {
            log.warn('logout tool error:', err);
            return errorResponse(err instanceof Error ? err.message : 'Logout failed', 'LOGOUT_FAILED');
        }
    });
    registerTool(mcp, 'auth_status', 'Report whether this machine is signed in to Rivet (server-validated, refreshing a stale token if possible). Returns { authenticated, email, userId, profile? }.', {
        // Accept (and ignore) an optional probe flag so clients that always send
        // an argument object don't trip schema validation.
        detailed: v3_1.z
            .boolean()
            .optional()
            .describe('Reserved; auth_status always returns full status.'),
    }, async () => {
        try {
            const profile = await authService.getValidatedProfile();
            const cm = (0, ConfigManager_1.getConfigManager)();
            if (!profile) {
                return jsonResponse({ authenticated: false });
            }
            return jsonResponse({
                authenticated: true,
                email: cm.getEmail(),
                userId: cm.getUserId(),
                profile: profile.profile,
            });
        }
        catch (err) {
            log.warn('auth_status tool error:', err);
            return errorResponse(err instanceof Error ? err.message : 'Status check failed', 'STATUS_FAILED');
        }
    });
}
//# sourceMappingURL=tools.js.map