// tools/license-gate.ts — Runtime license enforcement for MCP tool handlers.
// Provides getCurrentTier() with in-memory cache and withLicenseGate() wrapper.
import { createHash } from 'node:crypto';
import { licenseStore } from '../storage/index.js';
import * as usageStore from '../storage/usage-store.js';
import { resolveCurrentTier, checkToolAccess, isValidationFresh, isInGracePeriod, validateLicense, computeMachineFingerprint, } from '../engine/license-validator.js';
import { evaluateRateLimit, isTrialActive, recordUsageEvent, } from '../engine/usage-tracker/index.js';
// ---------------------------------------------------------------------------
// Owner token override (SHA-256 verified, not reversible)
// ---------------------------------------------------------------------------
const OWNER_TOKEN_HASH = '9f9e8410fd0a718540fe1e29f7beaa2506b539c8732915d8eddcb3bcff9b9465';
function checkOwnerOverride() {
    const token = process.env.SDD_OWNER_TOKEN;
    if (!token) {
        return null;
    }
    const hash = createHash('sha256').update(token.trim()).digest('hex');
    return hash === OWNER_TOKEN_HASH ? 'enterprise' : null;
}
// ---------------------------------------------------------------------------
// In-memory tier cache (avoids disk read on every tool call)
// ---------------------------------------------------------------------------
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
let cachedTier = null;
let cacheTimestamp = 0;
export async function getCurrentTier() {
    const now = Date.now();
    if (cachedTier !== null && now - cacheTimestamp < CACHE_TTL_MS) {
        return cachedTier;
    }
    const ownerTier = checkOwnerOverride();
    if (ownerTier) {
        cachedTier = ownerTier;
        cacheTimestamp = now;
        return cachedTier;
    }
    let state = await licenseStore.getLicenseState();
    // Synchronous online validation when license is stale (>7 days)
    if (state && !isValidationFresh(state)) {
        state = await performOnlineValidation(state);
    }
    cachedTier = resolveCurrentTier(state);
    cacheTimestamp = now;
    return cachedTier;
}
export function invalidateTierCache() {
    cachedTier = null;
    cacheTimestamp = 0;
}
// ---------------------------------------------------------------------------
// License gate wrapper
// ---------------------------------------------------------------------------
export function withLicenseGate(toolName, handler) {
    return async (args) => {
        const tier = await getCurrentTier();
        const check = checkToolAccess(toolName, tier);
        if (!check.allowed) {
            return { content: [{ type: 'text', text: check.message }], isError: true };
        }
        return handler(args);
    };
}
// ---------------------------------------------------------------------------
// Usage tracking wrapper
// ---------------------------------------------------------------------------
/**
 * Wraps a handler with rate limiting (free tier only) and usage event recording.
 * For free tier: checks daily limit + cooldown before executing.
 * For all tiers: records a usage event after execution (fire-and-forget).
 */
export function withUsageTracking(toolName, handler) {
    return async (args) => {
        const tier = await getCurrentTier();
        if (tier === 'free') {
            const today = new Date().toISOString().slice(0, 10);
            const [trialState, callCount, lastCallAt] = await Promise.all([
                usageStore.ensureTrialState(),
                usageStore.getDailyCallCount(today),
                usageStore.getLastCallTimestamp(),
            ]);
            const maxCalls = isTrialActive(trialState.firstRunAt) ? 50 : 30;
            const check = evaluateRateLimit(callCount, maxCalls, lastCallAt, new Date());
            if (!check.allowed) {
                return { content: [{ type: 'text', text: check.message }], isError: true };
            }
        }
        const result = await handler(args);
        const success = !result.isError;
        void usageStore.appendUsageEvent(recordUsageEvent(toolName, tier, success));
        return result;
    };
}
// ---------------------------------------------------------------------------
// Synchronous online validation (replaces background fire-and-forget)
// ---------------------------------------------------------------------------
async function performOnlineValidation(state) {
    try {
        const result = await validateLicense(state.licenseKey, state.instanceId);
        if (!result.valid) {
            // License revoked remotely — clear local state
            await licenseStore.deleteLicenseState();
            return null;
        }
        const refreshed = {
            ...state,
            lastValidatedAt: new Date().toISOString(),
            activationUsage: result.license_key.activation_usage,
            machineFingerprint: computeMachineFingerprint(),
        };
        await licenseStore.saveLicenseState(refreshed);
        return refreshed;
    }
    catch {
        // API unreachable — allow if within grace period (TTL + 3 days)
        if (isInGracePeriod(state)) {
            return state;
        }
        // Grace period expired — degrade to free
        return null;
    }
}
//# sourceMappingURL=license-gate.js.map