/**
 * Policy Inheritance System
 *
 * Enables policy hierarchies with inheritance and override capabilities.
 * Implements FR150 for Epic 4.
 *
 * Features:
 * - Parent-child policy relationships
 * - Rule inheritance and overrides
 * - Conflict resolution
 * - Inheritance chain validation
 *
 * @packageDocumentation
 */
import { createLogger } from '../../common/logger.js';
const logger = createLogger({ component: 'policy-inheritance' });
// =============================================================================
// Policy Inheritance Service
// =============================================================================
/**
 * Policy Inheritance Service
 *
 * Manages policy inheritance relationships and merging.
 */
export class PolicyInheritanceService {
    inheritanceMap = new Map();
    policyCache = new Map();
    /**
     * Set inheritance relationship
     */
    setInheritance(inheritance) {
        this.inheritanceMap.set(inheritance.policyId, inheritance);
        logger.debug({
            childId: inheritance.policyId,
            parentId: inheritance.parentPolicyId,
        }, 'Inheritance relationship set');
    }
    /**
     * Remove inheritance relationship
     */
    removeInheritance(policyId) {
        const removed = this.inheritanceMap.delete(policyId);
        if (removed) {
            logger.debug({ policyId }, 'Inheritance relationship removed');
        }
        return removed;
    }
    /**
     * Get parent policy ID
     */
    getParent(policyId) {
        return this.inheritanceMap.get(policyId)?.parentPolicyId;
    }
    /**
     * Get all children of a policy
     */
    getChildren(policyId) {
        const children = [];
        for (const [childId, inheritance] of this.inheritanceMap) {
            if (inheritance.parentPolicyId === policyId) {
                children.push(childId);
            }
        }
        return children;
    }
    /**
     * Get the full inheritance chain (from root to policy)
     */
    getInheritanceChain(policyId) {
        const chain = [];
        let currentId = policyId;
        while (currentId) {
            if (chain.includes(currentId)) {
                // Circular reference detected
                break;
            }
            chain.unshift(currentId);
            currentId = this.getParent(currentId);
        }
        return chain;
    }
    /**
     * Build the policy hierarchy tree
     */
    buildHierarchy(policies) {
        const policyMap = new Map(policies.map(p => [p.id, p]));
        const rootNodes = [];
        const nodeMap = new Map();
        // Create nodes
        for (const policy of policies) {
            nodeMap.set(policy.id, {
                policyId: policy.id,
                policyName: policy.name,
                parentId: this.getParent(policy.id),
                children: [],
                depth: 0,
            });
        }
        // Build tree
        for (const [policyId, node] of nodeMap) {
            if (node.parentId && nodeMap.has(node.parentId)) {
                const parent = nodeMap.get(node.parentId);
                parent.children.push(node);
                node.depth = parent.depth + 1;
            }
            else {
                rootNodes.push(node);
            }
        }
        // Calculate depths for non-root nodes
        this.calculateDepths(rootNodes);
        return rootNodes;
    }
    /**
     * Validate inheritance relationships
     */
    validateInheritance(policyId) {
        const errors = [];
        const warnings = [];
        let maxDepth = 0;
        // Check for circular references
        const visited = new Set();
        let currentId = policyId;
        let depth = 0;
        while (currentId) {
            if (visited.has(currentId)) {
                errors.push(`Circular inheritance detected: ${currentId} appears multiple times in the chain`);
                break;
            }
            visited.add(currentId);
            depth++;
            currentId = this.getParent(currentId);
        }
        maxDepth = depth;
        // Warn about deep hierarchies
        if (depth > 5) {
            warnings.push(`Deep inheritance hierarchy (${depth} levels). Consider flattening.`);
        }
        // Check for orphaned references
        const inheritance = this.inheritanceMap.get(policyId);
        if (inheritance) {
            if (!this.policyCache.has(inheritance.parentPolicyId)) {
                warnings.push(`Parent policy ${inheritance.parentPolicyId} not found in cache. May need to load.`);
            }
        }
        return {
            valid: errors.length === 0,
            errors,
            warnings,
            maxDepth,
        };
    }
    /**
     * Merge policy with its ancestors
     */
    mergeWithAncestors(policyId, childPolicy, getPolicyFn) {
        return this.mergePolicy(policyId, childPolicy, getPolicyFn);
    }
    /**
     * Internal merge implementation
     */
    async mergePolicy(policyId, policy, getPolicyFn) {
        const inheritance = this.inheritanceMap.get(policyId);
        // No inheritance - return as-is
        if (!inheritance) {
            const ruleSources = new Map();
            for (const rule of policy.rules) {
                ruleSources.set(rule.id, policyId);
            }
            return {
                definition: policy,
                ruleSources,
                conflicts: [],
                inheritanceChain: [policyId],
            };
        }
        // Get parent policy
        const parentPolicy = await getPolicyFn(inheritance.parentPolicyId);
        if (!parentPolicy) {
            throw new Error(`Parent policy not found: ${inheritance.parentPolicyId}`);
        }
        // Recursively merge parent
        const parentMerged = await this.mergePolicy(inheritance.parentPolicyId, parentPolicy, getPolicyFn);
        // Now merge child into parent
        const conflicts = [...parentMerged.conflicts];
        const ruleSources = new Map(parentMerged.ruleSources);
        // Start with parent rules (with priority offset)
        const mergedRules = parentMerged.definition.rules
            .filter(r => !inheritance.excludedRuleIds.includes(r.id))
            .map(r => ({
            ...r,
            priority: r.priority + inheritance.priorityOffset,
        }));
        // Child rules override parent rules with same ID
        const childRuleIds = new Set(policy.rules.map(r => r.id));
        for (const childRule of policy.rules) {
            const existingIndex = mergedRules.findIndex(r => r.id === childRule.id);
            if (existingIndex >= 0) {
                // Override
                conflicts.push({
                    type: 'rule_override',
                    ruleId: childRule.id,
                    sourcePolicyId: inheritance.parentPolicyId,
                    targetPolicyId: policyId,
                    resolution: 'child_wins',
                    description: `Rule "${childRule.name}" overrides parent rule`,
                });
                mergedRules[existingIndex] = childRule;
                ruleSources.set(childRule.id, policyId);
            }
            else {
                // Add new rule
                mergedRules.push(childRule);
                ruleSources.set(childRule.id, policyId);
            }
        }
        // Check for priority collisions
        const priorityMap = new Map();
        for (const rule of mergedRules) {
            const existing = priorityMap.get(rule.priority) ?? [];
            existing.push(rule);
            priorityMap.set(rule.priority, existing);
        }
        for (const [priority, rules] of priorityMap) {
            if (rules.length > 1) {
                conflicts.push({
                    type: 'priority_collision',
                    ruleId: rules.map(r => r.id).join(','),
                    sourcePolicyId: policyId,
                    targetPolicyId: policyId,
                    resolution: 'merged',
                    description: `Multiple rules with priority ${priority}: ${rules.map(r => r.name).join(', ')}`,
                });
            }
        }
        // Sort by priority
        mergedRules.sort((a, b) => a.priority - b.priority);
        // Merge target
        const mergedTarget = inheritance.inheritTarget
            ? this.mergeTargets(parentMerged.definition.target, policy.target)
            : policy.target;
        // Merge default action
        const defaultAction = inheritance.inheritDefaultAction
            ? parentMerged.definition.defaultAction
            : policy.defaultAction;
        const defaultReason = inheritance.inheritDefaultAction
            ? parentMerged.definition.defaultReason
            : policy.defaultReason;
        const definition = {
            version: policy.version,
            target: mergedTarget,
            rules: mergedRules,
            defaultAction,
            defaultReason,
            metadata: {
                ...parentMerged.definition.metadata,
                ...policy.metadata,
                inheritedFrom: inheritance.parentPolicyId,
            },
        };
        return {
            definition,
            ruleSources,
            conflicts,
            inheritanceChain: [...parentMerged.inheritanceChain, policyId],
        };
    }
    /**
     * Merge policy targets
     */
    mergeTargets(parent, child) {
        if (!parent)
            return child;
        if (!child)
            return parent;
        return {
            intentTypes: [...(parent.intentTypes ?? []), ...(child.intentTypes ?? [])].filter((v, i, a) => a.indexOf(v) === i),
            entityTypes: [...(parent.entityTypes ?? []), ...(child.entityTypes ?? [])].filter((v, i, a) => a.indexOf(v) === i),
            trustLevels: [...(parent.trustLevels ?? []), ...(child.trustLevels ?? [])].filter((v, i, a) => a.indexOf(v) === i),
            namespaces: [...(parent.namespaces ?? []), ...(child.namespaces ?? [])].filter((v, i, a) => a.indexOf(v) === i),
        };
    }
    /**
     * Calculate depths recursively
     */
    calculateDepths(nodes, depth = 0) {
        for (const node of nodes) {
            node.depth = depth;
            this.calculateDepths(node.children, depth + 1);
        }
    }
    /**
     * Cache a policy for inheritance lookups
     */
    cachePolicy(id, policy) {
        this.policyCache.set(id, policy);
    }
    /**
     * Clear the policy cache
     */
    clearCache() {
        this.policyCache.clear();
    }
}
// =============================================================================
// Factory Functions
// =============================================================================
/**
 * Create a policy inheritance service instance
 */
export function createPolicyInheritanceService() {
    return new PolicyInheritanceService();
}
//# sourceMappingURL=inheritance.js.map