/**
 * Permission Enforcer
 *
 * Validates sync operations against configured permissions before execution.
 * Uses the existing SyncSettings from config.json (set during specweave init).
 * Logs all permission decisions for audit.
 *
 * IMPORTANT: This integrates with the existing sync.settings configuration:
 * - canUpsertInternalItems: CREATE/UPDATE internal items in external tools
 * - canUpdateExternalItems: UPDATE items created externally
 * - canUpdateStatus: UPDATE status field for both internal and external items
 *
 * @module core/sync/permission-enforcer
 */
import { promises as fs } from 'fs';
import path from 'path';
import { DEFAULT_SYNC_SETTINGS, } from '../types/sync-settings.js';
import { consoleLogger } from '../../utils/logger.js';
/**
 * PermissionEnforcer - Validates sync operations before execution
 *
 * Uses the existing SyncSettings from config.json that are configured during
 * `specweave init`. This ensures consistency with user's sync preferences.
 *
 * Features:
 * - Check permissions before any sync operation
 * - Differentiate between internal and external items
 * - Log all decisions for audit
 */
export class PermissionEnforcer {
    constructor(options = {}) {
        this.settings = options.settings ?? { ...DEFAULT_SYNC_SETTINGS };
        const specweavePath = options.specweavePath ?? path.join(process.cwd(), '.specweave');
        this.logPath = path.join(specweavePath, 'logs', 'sync', 'permissions.jsonl');
        this.logger = options.logger ?? consoleLogger;
        this.enableLogging = options.enableLogging ?? true;
    }
    /**
     * Check if an operation is permitted for an item
     */
    checkPermission(platform, operation, itemId, origin = 'internal') {
        const allowed = this.isOperationAllowed(operation);
        const reason = allowed
            ? `Operation "${operation}" permitted for ${origin} item`
            : this.getDenialReason(operation);
        return {
            allowed,
            reason,
            platform,
            operation,
            itemId,
            origin,
            timestamp: new Date().toISOString(),
        };
    }
    /**
     * Check permission and log the decision
     *
     * @param platform - Target platform
     * @param operation - Operation to perform
     * @param itemId - Item ID being operated on
     * @param origin - Whether item is internal or external
     * @returns Permission result
     */
    async checkAndLog(platform, operation, itemId, origin = 'internal') {
        const result = this.checkPermission(platform, operation, itemId, origin);
        // Log the decision
        await this.logDecision(result);
        // Log to console if denied
        if (!result.allowed) {
            this.logger.debug(`Permission denied: ${result.reason} (${itemId})`);
        }
        return result;
    }
    /**
     * Convenience method: Check if upsert is allowed
     *
     * Automatically determines origin from itemId (ends with 'E' = external)
     *
     * @param platform - Target platform
     * @param itemId - Item ID (e.g., "FS-001" or "FS-001E")
     * @returns Permission result
     */
    checkUpsertPermission(platform, itemId) {
        const origin = this.detectOrigin(itemId);
        const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
        return this.checkPermission(platform, operation, itemId, origin);
    }
    /**
     * Convenience method: Check if status update is allowed
     *
     * @param platform - Target platform
     * @param itemId - Item ID
     * @returns Permission result
     */
    checkStatusUpdatePermission(platform, itemId) {
        const origin = this.detectOrigin(itemId);
        return this.checkPermission(platform, 'update-status', itemId, origin);
    }
    /**
     * Update settings at runtime
     *
     * @param settings - New sync settings (partial update supported)
     */
    updateSettings(settings) {
        this.settings = { ...this.settings, ...settings };
        this.logger.debug('Permission enforcer settings updated');
    }
    /**
     * Get current settings
     *
     * @returns Current sync settings
     */
    getSettings() {
        return { ...this.settings };
    }
    /**
     * Check if sync is enabled for any operation
     *
     * @returns true if any sync operation is enabled
     */
    isSyncEnabled() {
        return (this.settings.canUpsertInternalItems ||
            this.settings.canUpdateExternalItems ||
            this.settings.canUpdateStatus);
    }
    /**
     * Get a human-readable summary of current permissions
     */
    getPermissionsSummary() {
        const { canUpsertInternalItems, canUpdateExternalItems, canUpdateStatus } = this.settings;
        const parts = [];
        if (canUpsertInternalItems)
            parts.push('create/update internal items');
        if (canUpdateExternalItems)
            parts.push('update external items');
        if (canUpdateStatus)
            parts.push('update status');
        return {
            canCreateInternal: canUpsertInternalItems,
            canUpdateExternal: canUpdateExternalItems,
            canUpdateStatus,
            summary: parts.length > 0 ? `Allowed: ${parts.join(', ')}` : 'All sync disabled (read-only)',
        };
    }
    /**
     * Detect item origin from ID
     *
     * External items have 'E' suffix (e.g., FS-001E)
     */
    detectOrigin(itemId) {
        // External items end with 'E' or have '-E-' in the ID
        if (itemId.endsWith('E') || itemId.includes('-E-')) {
            return 'external';
        }
        return 'internal';
    }
    /**
     * Check if operation is allowed by current settings
     */
    isOperationAllowed(operation) {
        switch (operation) {
            case 'read':
                return true;
            case 'update-status':
                return this.settings.canUpdateStatus;
            case 'upsert-internal':
                return this.settings.canUpsertInternalItems;
            case 'upsert-external':
                return this.settings.canUpdateExternalItems;
            default:
                return false;
        }
    }
    /**
     * Get denial reason for logging
     */
    getDenialReason(operation) {
        const messages = {
            'update-status': 'Status updates disabled. Enable sync.settings.canUpdateStatus in config.',
            'upsert-internal': 'Creating/updating internal items disabled. Enable sync.settings.canUpsertInternalItems in config.',
            'upsert-external': 'Updating external items disabled. Enable sync.settings.canUpdateExternalItems in config.',
        };
        return messages[operation] ?? `Operation "${operation}" not permitted.`;
    }
    /**
     * Log permission decision to file
     */
    async logDecision(result) {
        if (!this.enableLogging) {
            return;
        }
        try {
            // Ensure directory exists
            const dir = path.dirname(this.logPath);
            await fs.mkdir(dir, { recursive: true });
            // Append to JSONL file
            const line = JSON.stringify(result) + '\n';
            await fs.appendFile(this.logPath, line, 'utf-8');
        }
        catch (error) {
            // Log errors but don't fail the operation
            this.logger.debug(`Failed to log permission decision: ${error}`);
        }
    }
    /**
     * Get log file path (for testing/debugging)
     */
    getLogPath() {
        return this.logPath;
    }
}
/**
 * Create a PermissionEnforcer from config.json
 *
 * Reads the existing sync.settings from config.json that was set during
 * `specweave init` and creates a PermissionEnforcer with those settings.
 *
 * @param specweavePath - Path to .specweave directory
 * @param logger - Optional logger
 * @returns PermissionEnforcer instance
 */
export async function createPermissionEnforcerFromConfig(specweavePath = path.join(process.cwd(), '.specweave'), logger) {
    const configPath = path.join(specweavePath, 'config.json');
    try {
        const content = await fs.readFile(configPath, 'utf-8');
        const config = JSON.parse(content);
        // Extract sync settings from config
        const settings = {
            canUpsertInternalItems: config?.sync?.settings?.canUpsertInternalItems ?? DEFAULT_SYNC_SETTINGS.canUpsertInternalItems,
            canUpdateExternalItems: config?.sync?.settings?.canUpdateExternalItems ?? DEFAULT_SYNC_SETTINGS.canUpdateExternalItems,
            canUpdateStatus: config?.sync?.settings?.canUpdateStatus ?? DEFAULT_SYNC_SETTINGS.canUpdateStatus,
        };
        return new PermissionEnforcer({
            settings,
            specweavePath,
            logger,
        });
    }
    catch {
        // Return enforcer with default (disabled) settings
        return new PermissionEnforcer({
            settings: DEFAULT_SYNC_SETTINGS,
            specweavePath,
            logger,
        });
    }
}
//# sourceMappingURL=permission-enforcer.js.map